@aryacode/dsh-local-llm 0.8.1 → 0.8.2

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.
@@ -126,11 +126,11 @@ Validation: focused discovery tests cover complete and incomplete shard groups,
126
126
 
127
127
  ## NInfer
128
128
 
129
- For Docker-free serving, add `ninfer.wsl` with `distribution`, an absolute Linux `binary` path and `libraryPath` (WSL driver directory first, then CUDA libraries). The same tuning, benchmark, readiness and log controls manage one WSL process group. PID, Linux start time, executable and command line must match the persisted owner before stopping. No Docker command runs in WSL mode; the existing image configuration remains available for rollback. Build and validate the binary with CUDA and FFmpeg before enabling this mode.
129
+ For Docker-free serving, add `ninfer.wsl` with `distribution`, an absolute Linux `binary` path and `libraryPath` (WSL driver directory first, then CUDA libraries). The same tuning, benchmark, readiness and log controls manage one WSL process group. PID, Linux start time, executable and command line must match the persisted owner before stopping. WSL mode starts and stops no Docker container; the existing image configuration remains available for rollback. Docker is consulted only to identify a running container that another launcher started from the same pinned image, artifact and port, and that container's log is served while it answers; an explicit takeover is the only path that stops it. Build and validate the binary with CUDA and FFmpeg before enabling this mode.
130
130
 
131
131
  Configure `ninfer` with an explicit install directory, verified artifact path, Docker image digest, container name and ownership label, port, modelName and validated defaults (context, prefill, hostKvMiB, draftTokens and vision). Servers → NInfer exposes Start, Stop, Restart, readiness, logs and tuning. Controls bind the model API to localhost and refuse foreign container IDs, labels or occupied ports. Settings can be saved only while stopped; start refuses other loaded engines. The pinned MTP profile uses rk4v4-e8 KV, draft windows up to three and disables n-gram chaining. Paired remote controls are not exposed for this engine.
132
132
 
133
- The AryaStrata page shows an NInfer view for these routes on the engine computer: lifecycle controls, tuning while stopped and the log tail. A paired page keeps the Strata-only view, and the routes themselves are unchanged.
133
+ The AryaStrata page shows an NInfer view for these routes on the engine computer: lifecycle controls, tuning while stopped and the log tail. A paired page keeps the Strata-only view, and the routes themselves are unchanged. A container started by another launcher is reported as External, with the same log and a takeover that stops only a container matching the pinned image, artifact and port.
134
134
 
135
135
  Verified NInfer artifacts appear as local models in Benchmarks. Choose 32K, 64K or 100K; larger requests fail before engines are interrupted. Runs translate thinking into top-level enable_thinking/reasoning_effort, omit llama.cpp chat_template_kwargs, and restore previous NInfer tuning and owned servers. Configured context and short image checks do not establish full-context retrieval or website quality.
136
136
 
package/README.md CHANGED
@@ -16,8 +16,14 @@ The **NInfer** view controls the local engine that serves the pinned Qwen3.8 den
16
16
 
17
17
  Start, stop and restart bind the model API to localhost. The engine loads for a while after Start, so the page reports `starting` until the model API answers. **NInfer tuning** is editable only while the engine is stopped, and **NInfer logs** reads the same owner-checked log tail. Tuning holds the context window, prefill chunk, host KV, MTP drafts, the **response output tokens** allowance, n-gram drafts and vision. The output allowance is shared by the engine and the Local model library chat model, so it must stay below the context window. The page never adopts a container or process it did not start.
18
18
 
19
+ An engine started somewhere else is reported as **External** rather than left with dead controls. When the container is provably this installation's engine — the pinned image, this artifact, this port and its NInfer label — the page offers **Take over engine**, which stops that container and starts the engine it manages; while it is external the view still reads that container's own log, so the live figures keep working. Anything the page cannot identify is named with where it was started and is never touched.
20
+
19
21
  The view is a live operations dashboard. Model state, VRAM use and GPU load come from the host sensors; decode and prefill throughput, speculative-decoding acceptance and weight-loading progress come from the engine's own log. A failed start shows the engine's reason — usually not enough VRAM while Strata holds the GPU — with a button to stop Strata and retry. **Open NInfer dashboard** opens the engine's own web interface.
20
22
 
23
+ **Connect your tools** states what a client needs and copies it: the OpenAI-compatible base URL, the chat-completions URL, the model name and the key the engine asks for (none on loopback), each with its own copy button, plus **Copy .env** for a ready environment block. A worked example switches between curl, Python and Node.js with the address and model already filled in. **Engine** lists the pinned artifact the page serves — context, response allowance, prefill chunk, host KV, MTP drafts, n-gram mode, vision and the runtime identity — so the numbers a client budgets against are on the same screen as the controls.
24
+
25
+ Each engine is published to the harness with what it actually is: its real context window, its input modalities and its thinking levels, read from the engine's own settings. The response allowance stays a ceiling the engine enforces when a request asks for it — the model advertises an 8K per-request output default — so **Chat output limits** raises that for a session that needs longer answers.
26
+
21
27
  The switch appears only on the engine computer. A page that controls a paired computer keeps the Strata-only view, and the NInfer routes answer only loopback callers. Only one engine can hold the GPU at a time, so starting either engine first stops the other one — and any owned llama.cpp or FreeToken server or loaded Ollama model — then starts the requested engine. A stop that cannot be completed refuses the start instead of double-booking the GPU; an engine started outside this Host is reported and never killed.
22
28
 
23
29
  Chat model discovery, Benchmarks and model sharing with a paired computer are unchanged. They read the same `ninfer` configuration.
package/lib/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  window.__ModuleLoader__.load({
2
- id: "dsh-local-llm",
2
+ id: "@aryacode/dsh-local-llm",
3
3
  factory: (require) => {
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
@@ -522,7 +522,7 @@ window.__ModuleLoader__.load({
522
522
  }
523
523
  //#endregion
524
524
  //#region src/client/theme.ts
525
- const css = ".lc{--bg:var(--dsw-alias-bg-base);--panel:var(--dsw-alias-bg-layer-1);--line:var(--dsw-alias-border-l1);--ink:var(--dsw-alias-label-primary);--muted:var(--dsw-alias-label-secondary);--blue:var(--dsw-alias-label-primary);--teal:var(--dsw-alias-state-success-primary);box-sizing:border-box;height:100%;overflow:auto;background:var(--bg);color:var(--ink);font:14px/1.5 var(--dsw-font-family-sans,system-ui,sans-serif);padding:30px 34px}.lc *{box-sizing:border-box}.lc button,.lc input,.lc select{font:inherit}.lc button{cursor:pointer}.lc button:disabled{opacity:.4;cursor:default}.lc a{color:var(--blue)}.lc h1,.lc h2,.lc h3,.lc p{margin:0}.lc h1{font-size:30px;letter-spacing:-1.1px;font-weight:500}.lc h2{font-size:18px;letter-spacing:-.3px}.lc h3{font-size:15px}.lc-muted{color:var(--muted)}.lc-header{display:flex;justify-content:space-between;align-items:center;gap:18px;margin-bottom:24px}.lc-eyebrow{color:var(--blue);font-size:10px;letter-spacing:2px;font-weight:500;text-transform:uppercase;margin-bottom:7px}.lc-brand{display:flex;align-items:center;gap:15px}.lc-mark{width:49px;height:49px;background:var(--dsw-alias-bg-layer-2);color:var(--ink);border:1px solid var(--line);border-radius:14px;display:grid;place-items:center;box-shadow:none}.lc-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 10px;border:1px solid var(--line);border-radius:20px;font-size:11px;color:var(--muted);white-space:nowrap}.lc-dot{height:6px;width:6px;border-radius:50%;background:currentColor}.lc-pill.good{color:var(--teal);background:color-mix(in srgb,var(--teal) 7%,var(--panel));border-color:color-mix(in srgb,var(--teal) 25%,var(--line))}.lc-pill.warm{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,var(--panel));border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 25%,var(--line))}.lc-tabs{display:flex;gap:6px;padding:5px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--line);border-radius:11px;width:fit-content;margin-bottom:23px}.lc-tab{display:flex;align-items:center;gap:8px;border:0;color:var(--muted);background:transparent;padding:9px 17px;border-radius:7px;font-size:12px}.lc-tab.active{color:var(--ink);background:var(--dsw-alias-interactive-bg-hover);box-shadow:none}.lc-grid{display:grid;gap:16px}.lc-kpis{grid-template-columns:repeat(4,minmax(0,1fr));margin-bottom:20px}.lc-card{border:1px solid var(--line);border-radius:14px;background:var(--panel);padding:20px}.lc-kpi-head{display:flex;justify-content:space-between;align-items:center;font-size:12px;color:var(--muted)}.lc-value{font-size:28px;font-weight:500;letter-spacing:-1px;margin:9px 0 3px}.lc-value small{font-size:13px;font-weight:400;color:var(--muted);letter-spacing:0}.lc-sub{font-size:11px;color:var(--muted)}.lc-bar{height:5px;background:var(--dsw-alias-border-l1);border-radius:8px;overflow:hidden;margin:14px 0 6px}.lc-bar span{height:100%;display:block;background:var(--ink);border-radius:8px}.lc-section{display:flex;align-items:center;justify-content:space-between;margin:23px 0 13px}.lc-section small{color:var(--muted);font-size:11px}.lc-charts{grid-template-columns:1.6fr 1fr}.lc-chart{width:100%;height:145px;display:block;margin-top:18px}.lc-chart-footer{display:flex;justify-content:space-between;color:var(--muted);font-size:10px}.lc-legend{display:flex;gap:16px;font-size:11px;color:var(--muted)}.lc-engine-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.lc-engine{position:relative;transition:transform .15s,border-color .15s;cursor:pointer;text-align:left;color:var(--ink)}.lc-engine:hover{transform:translateY(-2px);border-color:var(--dsw-alias-border-l2)}.lc-engine.selected{border-color:var(--dsw-alias-label-primary);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2)}.lc-engine .lc-logo{width:36px;height:36px;display:grid;place-items:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--line);border-radius:10px;margin-bottom:16px;font-size:16px;font-weight:500}.lc-engine h3{margin-bottom:4px}.lc-engine-state{position:absolute;top:22px;right:16px}.lc-engine-note{font-size:11px;color:var(--muted);margin-top:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-button{display:inline-flex;align-items:center;justify-content:center;gap:7px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-floating-fill);color:var(--dsw-alias-label-primary);padding:9px 14px;border-radius:8px;font-size:12px}.lc-button.primary{background:var(--dsw-alias-button-primary-fill);border-color:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-invert, var(--bg));font-weight:500}.lc-button.danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 30%,var(--line));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,var(--panel))}.lc-button.ghost{background:transparent}.lc-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:18px 0}.lc-detail-grid{grid-template-columns:1.5fr 1fr;margin-top:16px}.lc-facts{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin:20px 0}.lc-facts span{display:block;color:var(--muted);font-size:11px;margin-bottom:5px}.lc-facts strong{font-size:13px;font-weight:500}.lc-input{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);color:var(--ink);padding:10px 12px;border-radius:8px;min-width:0}.lc-input:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}.lc-search{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:18px 0}.lc-search input{flex:1;min-width:180px}.lc-model-grid{grid-template-columns:repeat(auto-fill,minmax(270px,1fr))}.lc-model-desc{font-size:12px;color:var(--muted);margin:9px 0 15px!important;min-height:36px}.lc-tags{display:flex;flex-wrap:wrap;gap:5px;margin:10px 0}.lc-tag{padding:3px 7px;background:var(--dsw-alias-bg-layer-2);color:var(--muted);border-radius:5px;font-size:10px}.lc table{width:100%;border-collapse:collapse;font-size:12px}.lc th{text-align:left;color:var(--muted);font-weight:500;font-size:10px;letter-spacing:1px;text-transform:uppercase;padding:14px 12px;border-bottom:1px solid var(--line)}.lc td{padding:14px 12px;border-bottom:1px solid var(--line)}.lc td .lc-button{padding:5px 10px;font-size:11px}.lc-code{font:12px ui-monospace,monospace;color:var(--ink)}.lc-terminal{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--line);padding:18px;border-radius:10px;white-space:pre-wrap;overflow:auto;max-height:440px;font:11px/1.7 ui-monospace,monospace;color:var(--muted);margin-top:18px}.lc-notice{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);padding:12px 16px;border-radius:9px;margin-bottom:16px;font-size:12px}.lc-notice.error{border-color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,var(--panel));color:var(--dsw-alias-state-error-primary)}.lc-empty{padding:45px;text-align:center;color:var(--muted);border:1px dashed var(--line);border-radius:12px}.lc-select-line{display:flex;gap:10px;align-items:center;margin-top:12px}.lc-select-line input,.lc-select-line select{flex:1;min-width:0}.lc-footer{margin:25px 0 5px;color:var(--muted);font-size:10px;display:flex;justify-content:space-between}.lc-field{display:block;color:var(--muted);font-size:12px;margin-top:14px}.lc-field input{width:100%;margin-top:6px}.lc svg{flex-shrink:0}.lc svg text{font:10px system-ui;fill:var(--muted)}@media(max-width:1150px){.lc{padding:24px}.lc-kpis,.lc-engine-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:700px){.lc-charts,.lc-detail-grid{grid-template-columns:1fr}.lc-header{align-items:flex-start}.lc h1{font-size:23px}.lc-tabs{flex-wrap:wrap;width:100%}.lc-tab{padding:8px}.lc-facts{grid-template-columns:1fr}.lc table{display:block;overflow:auto}}\n.lb-setup{grid-template-columns:1fr 1fr 1fr}.lb-options{display:flex;flex-direction:column;gap:14px;margin-top:18px;max-height:290px;overflow:auto}.lb-option{display:flex;gap:10px;align-items:flex-start;font-size:13px;cursor:pointer}.lb-option input{margin-top:4px;accent-color:var(--ink);flex-shrink:0}.lb-option small{display:block;color:var(--muted);font-size:11px;margin-top:4px}.lb-settings{display:grid;grid-template-columns:1fr 1fr;gap:0 12px}.lb-settings select{display:block;width:100%;margin-top:6px}.lb-settings .lb-option{grid-column:1/-1;margin-top:16px}.lb-tasks{grid-template-columns:repeat(3,1fr);margin-top:20px;gap:20px}.lb-tasks details{margin-top:8px;margin-left:23px}.lb-summary{grid-template-columns:repeat(auto-fit,minmax(230px,1fr));margin-top:16px}.lb-table{overflow:auto}.lb-table td{vertical-align:top;min-width:90px}.lb-table td:first-child{min-width:210px}.lb-table td:nth-child(6){max-width:300px}.lb-previews{grid-template-columns:repeat(auto-fit,minmax(400px,1fr))}.lb-preview{margin-top:16px;overflow:auto;border:1px solid var(--line);border-radius:9px;background:var(--bg);max-height:650px}.lb-preview img{display:block;width:100%;height:auto}.lb-preview iframe{display:block;width:100%;height:600px;border:0;background:var(--bg)}.lb-preview.mobile{max-width:390px;margin-inline:auto}.lb .lc-field>select{margin-left:10px}.lb-settings .lc-field>select{margin-left:0}@media(max-width:1100px){.lb-setup{grid-template-columns:1fr 1fr}.lb-setup>section:last-child{grid-column:1/-1}}@media(max-width:700px){.lb-setup,.lb-tasks,.lb-previews{grid-template-columns:1fr}.lb-setup>section:last-child{grid-column:auto}.lb-preview iframe{height:500px}}\n.lb-history-actions{gap:12px;flex-wrap:wrap}.lb-comparison{margin-bottom:18px}.lb-compare-picker{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;max-height:220px;overflow:auto;margin:18px 0}.lb-comparison-table table{min-width:760px}.lb-comparison-table th:not(:first-child),.lb-comparison-table td{min-width:230px;max-width:340px}.lb-comparison-table th small{display:block;margin-top:5px;letter-spacing:0;text-transform:none}.lb-comparison-table tbody th{min-width:210px;text-transform:none;letter-spacing:0;font-size:12px}.lb-comparison-table caption{text-align:left;padding:10px 0}.lb-comparison-table thead th{position:sticky;top:0;background:var(--panel)}\n\n.lb-config{grid-template-columns:repeat(3,minmax(0,1fr))}.lb-config .lc-field{min-width:0}.lc-field select{max-width:100%}.lc-engine-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.lc .lb-tasks>div{min-width:0;overflow-wrap:anywhere}.lc button:focus-visible,.lc input:focus-visible,.lc select:focus-visible{outline:2px solid var(--ink);outline-offset:3px}@media(max-width:1100px){.lb-config{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:700px){.lb-config,.lc-engine-grid,.lb-settings{grid-template-columns:1fr}.lc{padding:16px}.lc-footer{flex-wrap:wrap;gap:8px}}\n\n.lc h1,.lc h2,.lc h3,.lc strong{font-weight:500}.lb-config .lc-sub{grid-column:1/-1;margin-top:10px}\n.ld-heading{display:flex;justify-content:space-between;align-items:center;gap:24px;margin:8px 0 24px}.ld-heading h2{font-size:26px;margin:8px 0}.ld-hardware{display:flex;gap:20px;flex-wrap:wrap;padding:14px 18px;background:var(--panel);border:1px solid var(--line);border-radius:10px;font-size:12px;color:var(--muted)}.ld-hardware strong{color:var(--ink)}.ld-search{display:grid;grid-template-columns:210px minmax(200px,1fr) auto;gap:10px;padding:14px;background:var(--panel);border:1px solid var(--line);border-radius:12px}.ld-toolbar{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:16px 0 22px}.ld-suggestions,.ld-filters{display:flex;gap:8px;flex-wrap:wrap}.ld-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:22px;align-items:start}.ld-workspace.has-selection{grid-template-columns:minmax(0,1fr) minmax(320px,390px)}.ld-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px}.ld-model{display:flex;flex-direction:column;gap:10px;padding:20px;min-height:220px;transition:border-color .15s}.ld-model.is-selected{border-color:var(--ink);background:var(--dsw-alias-bg-layer-2)}.ld-model-top{display:flex;gap:6px}.ld-model-title{font-size:15px;font-weight:500;color:var(--ink);overflow-wrap:anywhere;text-decoration:none;line-height:1.45}.ld-model-title:hover,.ld-detail a:hover{text-decoration:underline}.ld-publisher{margin-top:-6px!important}.ld-description{font-size:12px;color:var(--muted);line-height:1.5;overflow-wrap:anywhere}.ld-model-meta{display:flex;gap:12px;flex-wrap:wrap;font-size:11px;color:var(--muted);margin-top:auto}.ld-model>.lc-button{align-self:flex-start;margin-top:4px}.ld-results-label{font-size:12px;color:var(--muted);min-height:22px;margin-bottom:8px}.ld-detail{position:sticky;top:16px;padding:22px;max-height:calc(100vh - 50px);overflow:auto}.ld-detail-header{display:flex;justify-content:space-between;align-items:center;gap:8px}.ld-detail h3{margin:16px 0;overflow-wrap:anywhere;line-height:1.5}.ld-detail a{color:var(--ink)}.ld-budget{display:grid;grid-template-columns:1fr 1fr;gap:12px}.ld-budget small,.ld-version small{display:block;color:var(--muted);font-size:11px;margin-top:4px}.ld-detail select{display:block;width:100%;margin-top:6px}.ld-versions{border:0;padding:0;margin:20px 0}.ld-versions legend{font-size:12px;color:var(--muted);margin-bottom:10px}.ld-version{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:9px;padding:12px;margin-top:8px;cursor:pointer;font-size:13px}.ld-version.is-selected{border-color:var(--ink);background:var(--dsw-alias-bg-layer-2)}.ld-version>span{flex:1;min-width:0;overflow-wrap:anywhere}.ld-version input{accent-color:var(--ink)}.ld-total{display:flex;justify-content:space-between;gap:12px;padding-top:18px;margin-top:18px;border-top:1px solid var(--line);font-size:14px}.ld-fit{font-size:12px;margin:10px 0;color:var(--ink)}.ld-detail>.lc-button,.ld-detail .lc-button.primary{width:100%;margin:16px 0 12px}.ld-file-list{margin:16px 0;font-size:12px;color:var(--muted)}.ld-file-list summary{cursor:pointer}.ld-file-list div{display:flex;justify-content:space-between;gap:10px;padding:8px 0;border-bottom:1px solid var(--line)}.ld-file-list div span:first-child{overflow-wrap:anywhere;min-width:0}.ld-file-list div span:last-child{white-space:nowrap}.ld-skeleton{height:220px;display:flex;flex-direction:column;gap:20px}.ld-skeleton span{height:16px;background:var(--dsw-alias-bg-layer-2);border-radius:4px}.ld-skeleton span:nth-child(2){width:65%}.ld-skeleton span:last-child{width:40%;margin-top:auto}.ld a:focus-visible{outline:2px solid var(--ink);outline-offset:3px}@media(max-width:1000px){.ld-workspace.has-selection{grid-template-columns:1fr}.ld-detail{position:static;max-height:none;grid-row:1}.ld-heading{align-items:flex-start;flex-direction:column}.ld-search{grid-template-columns:180px 1fr}.ld-search .lc-button{grid-column:1/-1}}@media(max-width:600px){.ld-search{grid-template-columns:1fr}.ld-cards{grid-template-columns:1fr}.ld-budget{grid-template-columns:1fr}.ld-filters{width:100%}.ld-filters select{flex:1;min-width:0}.ld-heading h2{font-size:23px}}\n\n.lc-tune-control{min-width:0;display:grid;gap:8px;color:var(--dsw-alias-label-primary);font-size:13px}.lc-tune-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.lc-tune-heading output{font-weight:500;font-variant-numeric:tabular-nums}.lc-tune-row{display:flex;align-items:center;gap:12px;min-width:0}.lc-tune-range{flex:1;min-width:0;height:24px;accent-color:var(--dsw-alias-label-primary);cursor:pointer}.lc-tune-exact{width:100px;flex:0 0 100px;min-width:0;text-align:right}.lc-tune-scale{display:flex;justify-content:space-between;padding-right:112px;font-size:11px;color:var(--dsw-alias-label-secondary)}.lc-tune-choices{display:flex;gap:6px;flex-wrap:wrap}.lc-tune-choices .lc-button{padding:5px 9px;font-size:12px}.lc-tune-range:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px;border-radius:6px}.lc-tune-control .lc-sub{margin:0}.lc-tune-range:disabled{cursor:default}\n.lc-tune-control{align-self:start;align-content:start}\n.lb-config .lb-settings{grid-template-columns:1fr}\n\n.lc{padding:24px;max-width:none;margin:0;width:100%;align-self:stretch;min-width:0}.lc-card{background:var(--dsw-alias-bg-base);border-radius:12px}.lc h1{font-size:26px;letter-spacing:-.6px}.lc h2,.lc h3{font-weight:500}.lc-sub{font-size:12px;line-height:1.6}.lc-tabs{max-width:100%;flex-wrap:wrap;background:transparent;padding:0;border:0;gap:4px}.lc-tab{padding:10px 14px;font-size:13px}.lc-tab.active{background:var(--dsw-alias-bg-layer-2)}.lc-header{margin:24px 0 20px}.lc-model-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:16px}.lc-model-card{display:flex;flex-direction:column;align-items:flex-start;gap:12px;min-width:0}.lc-model-card h3,.lc-model-card p{overflow-wrap:anywhere}.lc-model-card .lc-actions{margin-top:auto;flex-wrap:wrap}.lc-health-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.lc-health-meter{margin:18px 0}.lc-health-meter>div{display:flex;justify-content:space-between;gap:12px;font-size:13px}.lc-health-meter strong{font-weight:500}.lc-health-meter progress{display:block;width:100%;height:7px;margin:9px 0;accent-color:var(--dsw-alias-label-primary)}.lc-health-meter small{font-size:12px;color:var(--dsw-alias-label-secondary)}.lc-core-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.lc-core-grid>div{display:flex;align-items:center;gap:6px;font-size:10px;color:var(--dsw-alias-label-secondary)}.lc-core-grid progress{width:100%;height:5px;accent-color:var(--dsw-alias-state-success-primary)}.lc-network-row{display:grid;gap:4px;padding:12px 0;border-bottom:1px solid var(--dsw-alias-border-l1);font-size:12px}.lc-network-row strong{font-weight:500}.lc-connection{border-bottom:1px solid var(--dsw-alias-border-l1);padding-bottom:18px}.lc-connection-bar{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap}.lc-connection-bar strong{display:block;overflow-wrap:anywhere;font-size:13px;font-weight:500}.lc-connection-fields{grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:16px;margin:18px 0}.lc-connection .lc-actions{flex-wrap:wrap;margin:0}.lc-connection-setup{margin-top:18px}.lc-connection-setup h3{margin-top:20px}.lc-connection input{width:100%;min-width:0}.lc-steps{padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.9}.lc-webui-link{display:inline-flex;text-decoration:none;margin:12px 0}.lc-engine-setup{margin-bottom:24px}.lc-engine-setup .lc-card p{margin:12px 0}.lc-engine-setup summary{cursor:pointer}.lc .lc-facts>div{min-width:0}.lc .lc-facts strong{overflow-wrap:anywhere}.lc-empty h3{margin-bottom:8px}.lc-empty .lc-button{margin-top:16px}.lc button:focus-visible,.lc a:focus-visible,.lc input:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}\n@media(max-width:800px){.lc-health-grid{grid-template-columns:1fr}.lc{padding:16px}.lc-header{align-items:flex-start}.lc-connection-bar{align-items:flex-start}.lc-tabs{gap:3px}.lc-tab{padding:9px 11px}.lc-section{gap:12px;flex-wrap:wrap}.lc-header h1{font-size:23px}.lc-header .lc-mark{display:none}.lc-connection-bar .lc-actions{width:100%}.lc-network-row{overflow-wrap:anywhere}}\n.lc-ninfer-hero{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px;align-items:start}\n.lc-ninfer-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}\n.lc-ninfer-kpis{grid-template-columns:repeat(4,minmax(0,1fr))}\n.lc-pill.bad{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,var(--panel));border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 25%,var(--line))}\n@media(max-width:1150px){.lc-ninfer-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}\n@media(max-width:700px){.lc-ninfer-hero{grid-template-columns:1fr}.lc-ninfer-actions{justify-content:flex-start}}\n@media(max-width:520px){.lc-ninfer-kpis{grid-template-columns:1fr}}\n";
525
+ const css = ".lc{--bg:var(--dsw-alias-bg-base);--panel:var(--dsw-alias-bg-layer-1);--line:var(--dsw-alias-border-l1);--ink:var(--dsw-alias-label-primary);--muted:var(--dsw-alias-label-secondary);--blue:var(--dsw-alias-label-primary);--teal:var(--dsw-alias-state-success-primary);box-sizing:border-box;height:100%;overflow:auto;background:var(--bg);color:var(--ink);font:14px/1.5 var(--dsw-font-family-sans,system-ui,sans-serif);padding:30px 34px}.lc *{box-sizing:border-box}.lc button,.lc input,.lc select{font:inherit}.lc button{cursor:pointer}.lc button:disabled{opacity:.4;cursor:default}.lc a{color:var(--blue)}.lc h1,.lc h2,.lc h3,.lc p{margin:0}.lc h1{font-size:30px;letter-spacing:-1.1px;font-weight:500}.lc h2{font-size:18px;letter-spacing:-.3px}.lc h3{font-size:15px}.lc-muted{color:var(--muted)}.lc-header{display:flex;justify-content:space-between;align-items:center;gap:18px;margin-bottom:24px}.lc-eyebrow{color:var(--blue);font-size:10px;letter-spacing:2px;font-weight:500;text-transform:uppercase;margin-bottom:7px}.lc-brand{display:flex;align-items:center;gap:15px}.lc-mark{width:49px;height:49px;background:var(--dsw-alias-bg-layer-2);color:var(--ink);border:1px solid var(--line);border-radius:14px;display:grid;place-items:center;box-shadow:none}.lc-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 10px;border:1px solid var(--line);border-radius:20px;font-size:11px;color:var(--muted);white-space:nowrap}.lc-dot{height:6px;width:6px;border-radius:50%;background:currentColor}.lc-pill.good{color:var(--teal);background:color-mix(in srgb,var(--teal) 7%,var(--panel));border-color:color-mix(in srgb,var(--teal) 25%,var(--line))}.lc-pill.warm{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 7%,var(--panel));border-color:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 25%,var(--line))}.lc-tabs{display:flex;gap:6px;padding:5px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--line);border-radius:11px;width:fit-content;margin-bottom:23px}.lc-tab{display:flex;align-items:center;gap:8px;border:0;color:var(--muted);background:transparent;padding:9px 17px;border-radius:7px;font-size:12px}.lc-tab.active{color:var(--ink);background:var(--dsw-alias-interactive-bg-hover);box-shadow:none}.lc-grid{display:grid;gap:16px}.lc-kpis{grid-template-columns:repeat(4,minmax(0,1fr));margin-bottom:20px}.lc-card{border:1px solid var(--line);border-radius:14px;background:var(--panel);padding:20px}.lc-kpi-head{display:flex;justify-content:space-between;align-items:center;font-size:12px;color:var(--muted)}.lc-value{font-size:28px;font-weight:500;letter-spacing:-1px;margin:9px 0 3px}.lc-value small{font-size:13px;font-weight:400;color:var(--muted);letter-spacing:0}.lc-sub{font-size:11px;color:var(--muted)}.lc-bar{height:5px;background:var(--dsw-alias-border-l1);border-radius:8px;overflow:hidden;margin:14px 0 6px}.lc-bar span{height:100%;display:block;background:var(--ink);border-radius:8px}.lc-section{display:flex;align-items:center;justify-content:space-between;margin:23px 0 13px}.lc-section small{color:var(--muted);font-size:11px}.lc-charts{grid-template-columns:1.6fr 1fr}.lc-chart{width:100%;max-width:760px;height:145px;display:block;margin:18px auto 0}.lc-chart-footer{display:flex;justify-content:space-between;color:var(--muted);font-size:10px}.lc-legend{display:flex;gap:16px;font-size:11px;color:var(--muted)}.lc-engine-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.lc-engine{position:relative;transition:transform .15s,border-color .15s;cursor:pointer;text-align:left;color:var(--ink)}.lc-engine:hover{transform:translateY(-2px);border-color:var(--dsw-alias-border-l2)}.lc-engine.selected{border-color:var(--dsw-alias-label-primary);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2)}.lc-engine .lc-logo{width:36px;height:36px;display:grid;place-items:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--line);border-radius:10px;margin-bottom:16px;font-size:16px;font-weight:500}.lc-engine h3{margin-bottom:4px}.lc-engine-state{position:absolute;top:22px;right:16px}.lc-engine-note{font-size:11px;color:var(--muted);margin-top:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-button{display:inline-flex;align-items:center;justify-content:center;gap:7px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-floating-fill);color:var(--dsw-alias-label-primary);padding:9px 14px;border-radius:8px;font-size:12px}.lc-button.primary{background:var(--dsw-alias-button-primary-fill);border-color:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-invert, var(--bg));font-weight:500}.lc-button.danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 30%,var(--line));background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,var(--panel))}.lc-button.ghost{background:transparent}.lc-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:18px 0}.lc-detail-grid{grid-template-columns:1.5fr 1fr;margin-top:16px}.lc-facts{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin:20px 0}.lc-facts span{display:block;color:var(--muted);font-size:11px;margin-bottom:5px}.lc-facts strong{font-size:13px;font-weight:500}.lc-input{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);color:var(--ink);padding:10px 12px;border-radius:8px;min-width:0}.lc-input:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}.lc-search{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:18px 0}.lc-search input{flex:1;min-width:180px}.lc-model-grid{grid-template-columns:repeat(auto-fill,minmax(270px,1fr))}.lc-model-desc{font-size:12px;color:var(--muted);margin:9px 0 15px!important;min-height:36px}.lc-tags{display:flex;flex-wrap:wrap;gap:5px;margin:10px 0}.lc-tag{padding:3px 7px;background:var(--dsw-alias-bg-layer-2);color:var(--muted);border-radius:5px;font-size:10px}.lc table{width:100%;border-collapse:collapse;font-size:12px}.lc th{text-align:left;color:var(--muted);font-weight:500;font-size:10px;letter-spacing:1px;text-transform:uppercase;padding:14px 12px;border-bottom:1px solid var(--line)}.lc td{padding:14px 12px;border-bottom:1px solid var(--line)}.lc td .lc-button{padding:5px 10px;font-size:11px}.lc-code{font:12px ui-monospace,monospace;color:var(--ink)}.lc-terminal{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--line);padding:18px;border-radius:10px;white-space:pre-wrap;overflow:auto;max-height:440px;font:11px/1.7 ui-monospace,monospace;color:var(--muted);margin-top:18px}.lc-notice{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);padding:12px 16px;border-radius:9px;margin-bottom:16px;font-size:12px}.lc-notice.error{border-color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,var(--panel));color:var(--dsw-alias-state-error-primary)}.lc-empty{padding:45px;text-align:center;color:var(--muted);border:1px dashed var(--line);border-radius:12px}.lc-select-line{display:flex;gap:10px;align-items:center;margin-top:12px}.lc-select-line input,.lc-select-line select{flex:1;min-width:0}.lc-footer{margin:25px 0 5px;color:var(--muted);font-size:10px;display:flex;justify-content:space-between}.lc-field{display:block;color:var(--muted);font-size:12px;margin-top:14px}.lc-field input{width:100%;margin-top:6px}.lc svg{flex-shrink:0}.lc svg text{font:10px system-ui;fill:var(--muted)}@media(max-width:1150px){.lc{padding:24px}.lc-kpis,.lc-engine-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:700px){.lc-charts,.lc-detail-grid{grid-template-columns:1fr}.lc-header{align-items:flex-start}.lc h1{font-size:23px}.lc-tabs{flex-wrap:wrap;width:100%}.lc-tab{padding:8px}.lc-facts{grid-template-columns:1fr}.lc table{display:block;overflow:auto}}\n.lb-setup{grid-template-columns:1fr 1fr 1fr}.lb-options{display:flex;flex-direction:column;gap:14px;margin-top:18px;max-height:290px;overflow:auto}.lb-option{display:flex;gap:10px;align-items:flex-start;font-size:13px;cursor:pointer}.lb-option input{margin-top:4px;accent-color:var(--ink);flex-shrink:0}.lb-option small{display:block;color:var(--muted);font-size:11px;margin-top:4px}.lb-settings{display:grid;grid-template-columns:1fr 1fr;gap:0 12px}.lb-settings select{display:block;width:100%;margin-top:6px}.lb-settings .lb-option{grid-column:1/-1;margin-top:16px}.lb-tasks{grid-template-columns:repeat(3,1fr);margin-top:20px;gap:20px}.lb-tasks details{margin-top:8px;margin-left:23px}.lb-summary{grid-template-columns:repeat(auto-fit,minmax(230px,1fr));margin-top:16px}.lb-table{overflow:auto}.lb-table td{vertical-align:top;min-width:90px}.lb-table td:first-child{min-width:210px}.lb-table td:nth-child(6){max-width:300px}.lb-previews{grid-template-columns:repeat(auto-fit,minmax(400px,1fr))}.lb-preview{margin-top:16px;overflow:auto;border:1px solid var(--line);border-radius:9px;background:var(--bg);max-height:650px}.lb-preview img{display:block;width:100%;height:auto}.lb-preview iframe{display:block;width:100%;height:600px;border:0;background:var(--bg)}.lb-preview.mobile{max-width:390px;margin-inline:auto}.lb .lc-field>select{margin-left:10px}.lb-settings .lc-field>select{margin-left:0}@media(max-width:1100px){.lb-setup{grid-template-columns:1fr 1fr}.lb-setup>section:last-child{grid-column:1/-1}}@media(max-width:700px){.lb-setup,.lb-tasks,.lb-previews{grid-template-columns:1fr}.lb-setup>section:last-child{grid-column:auto}.lb-preview iframe{height:500px}}\n.lb-history-actions{gap:12px;flex-wrap:wrap}.lb-comparison{margin-bottom:18px}.lb-compare-picker{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;max-height:220px;overflow:auto;margin:18px 0}.lb-comparison-table table{min-width:760px}.lb-comparison-table th:not(:first-child),.lb-comparison-table td{min-width:230px;max-width:340px}.lb-comparison-table th small{display:block;margin-top:5px;letter-spacing:0;text-transform:none}.lb-comparison-table tbody th{min-width:210px;text-transform:none;letter-spacing:0;font-size:12px}.lb-comparison-table caption{text-align:left;padding:10px 0}.lb-comparison-table thead th{position:sticky;top:0;background:var(--panel)}\n\n.lb-config{grid-template-columns:repeat(3,minmax(0,1fr))}.lb-config .lc-field{min-width:0}.lc-field select{max-width:100%}.lc-engine-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.lc .lb-tasks>div{min-width:0;overflow-wrap:anywhere}.lc button:focus-visible,.lc input:focus-visible,.lc select:focus-visible{outline:2px solid var(--ink);outline-offset:3px}@media(max-width:1100px){.lb-config{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:700px){.lb-config,.lc-engine-grid,.lb-settings{grid-template-columns:1fr}.lc{padding:16px}.lc-footer{flex-wrap:wrap;gap:8px}}\n\n.lc h1,.lc h2,.lc h3,.lc strong{font-weight:500}.lb-config .lc-sub{grid-column:1/-1;margin-top:10px}\n.ld-heading{display:flex;justify-content:space-between;align-items:center;gap:24px;margin:8px 0 24px}.ld-heading h2{font-size:26px;margin:8px 0}.ld-hardware{display:flex;gap:20px;flex-wrap:wrap;padding:14px 18px;background:var(--panel);border:1px solid var(--line);border-radius:10px;font-size:12px;color:var(--muted)}.ld-hardware strong{color:var(--ink)}.ld-search{display:grid;grid-template-columns:210px minmax(200px,1fr) auto;gap:10px;padding:14px;background:var(--panel);border:1px solid var(--line);border-radius:12px}.ld-toolbar{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:16px 0 22px}.ld-suggestions,.ld-filters{display:flex;gap:8px;flex-wrap:wrap}.ld-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:22px;align-items:start}.ld-workspace.has-selection{grid-template-columns:minmax(0,1fr) minmax(320px,390px)}.ld-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px}.ld-model{display:flex;flex-direction:column;gap:10px;padding:20px;min-height:220px;transition:border-color .15s}.ld-model.is-selected{border-color:var(--ink);background:var(--dsw-alias-bg-layer-2)}.ld-model-top{display:flex;gap:6px}.ld-model-title{font-size:15px;font-weight:500;color:var(--ink);overflow-wrap:anywhere;text-decoration:none;line-height:1.45}.ld-model-title:hover,.ld-detail a:hover{text-decoration:underline}.ld-publisher{margin-top:-6px!important}.ld-description{font-size:12px;color:var(--muted);line-height:1.5;overflow-wrap:anywhere}.ld-model-meta{display:flex;gap:12px;flex-wrap:wrap;font-size:11px;color:var(--muted);margin-top:auto}.ld-model>.lc-button{align-self:flex-start;margin-top:4px}.ld-results-label{font-size:12px;color:var(--muted);min-height:22px;margin-bottom:8px}.ld-detail{position:sticky;top:16px;padding:22px;max-height:calc(100vh - 50px);overflow:auto}.ld-detail-header{display:flex;justify-content:space-between;align-items:center;gap:8px}.ld-detail h3{margin:16px 0;overflow-wrap:anywhere;line-height:1.5}.ld-detail a{color:var(--ink)}.ld-budget{display:grid;grid-template-columns:1fr 1fr;gap:12px}.ld-budget small,.ld-version small{display:block;color:var(--muted);font-size:11px;margin-top:4px}.ld-detail select{display:block;width:100%;margin-top:6px}.ld-versions{border:0;padding:0;margin:20px 0}.ld-versions legend{font-size:12px;color:var(--muted);margin-bottom:10px}.ld-version{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:9px;padding:12px;margin-top:8px;cursor:pointer;font-size:13px}.ld-version.is-selected{border-color:var(--ink);background:var(--dsw-alias-bg-layer-2)}.ld-version>span{flex:1;min-width:0;overflow-wrap:anywhere}.ld-version input{accent-color:var(--ink)}.ld-total{display:flex;justify-content:space-between;gap:12px;padding-top:18px;margin-top:18px;border-top:1px solid var(--line);font-size:14px}.ld-fit{font-size:12px;margin:10px 0;color:var(--ink)}.ld-detail>.lc-button,.ld-detail .lc-button.primary{width:100%;margin:16px 0 12px}.ld-file-list{margin:16px 0;font-size:12px;color:var(--muted)}.ld-file-list summary{cursor:pointer}.ld-file-list div{display:flex;justify-content:space-between;gap:10px;padding:8px 0;border-bottom:1px solid var(--line)}.ld-file-list div span:first-child{overflow-wrap:anywhere;min-width:0}.ld-file-list div span:last-child{white-space:nowrap}.ld-skeleton{height:220px;display:flex;flex-direction:column;gap:20px}.ld-skeleton span{height:16px;background:var(--dsw-alias-bg-layer-2);border-radius:4px}.ld-skeleton span:nth-child(2){width:65%}.ld-skeleton span:last-child{width:40%;margin-top:auto}.ld a:focus-visible{outline:2px solid var(--ink);outline-offset:3px}@media(max-width:1000px){.ld-workspace.has-selection{grid-template-columns:1fr}.ld-detail{position:static;max-height:none;grid-row:1}.ld-heading{align-items:flex-start;flex-direction:column}.ld-search{grid-template-columns:180px 1fr}.ld-search .lc-button{grid-column:1/-1}}@media(max-width:600px){.ld-search{grid-template-columns:1fr}.ld-cards{grid-template-columns:1fr}.ld-budget{grid-template-columns:1fr}.ld-filters{width:100%}.ld-filters select{flex:1;min-width:0}.ld-heading h2{font-size:23px}}\n\n.lc-tune-control{min-width:0;display:grid;gap:8px;color:var(--dsw-alias-label-primary);font-size:13px}.lc-tune-heading{display:flex;align-items:center;justify-content:space-between;gap:12px}.lc-tune-heading output{font-weight:500;font-variant-numeric:tabular-nums}.lc-tune-row{display:flex;align-items:center;gap:12px;min-width:0}.lc-tune-range{flex:1;min-width:0;height:24px;accent-color:var(--dsw-alias-label-primary);cursor:pointer}.lc-tune-exact{width:100px;flex:0 0 100px;min-width:0;text-align:right}.lc-tune-scale{display:flex;justify-content:space-between;padding-right:112px;font-size:11px;color:var(--dsw-alias-label-secondary)}.lc-tune-choices{display:flex;gap:6px;flex-wrap:wrap}.lc-tune-choices .lc-button{padding:5px 9px;font-size:12px}.lc-tune-range:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px;border-radius:6px}.lc-tune-control .lc-sub{margin:0}.lc-tune-range:disabled{cursor:default}\n.lc-tune-control{align-self:start;align-content:start}\n.lb-config .lb-settings{grid-template-columns:1fr}\n\n.lc{padding:24px;max-width:none;margin:0;width:100%;align-self:stretch;min-width:0}.lc-card{background:var(--dsw-alias-bg-base);border-radius:12px}.lc h1{font-size:26px;letter-spacing:-.6px}.lc h2,.lc h3{font-weight:500}.lc-sub{font-size:12px;line-height:1.6}.lc-tabs{max-width:100%;flex-wrap:wrap;background:transparent;padding:0;border:0;gap:4px}.lc-tab{padding:10px 14px;font-size:13px}.lc-tab.active{background:var(--dsw-alias-bg-layer-2)}.lc-header{margin:24px 0 20px}.lc-model-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:16px}.lc-model-card{display:flex;flex-direction:column;align-items:flex-start;gap:12px;min-width:0}.lc-model-card h3,.lc-model-card p{overflow-wrap:anywhere}.lc-model-card .lc-actions{margin-top:auto;flex-wrap:wrap}.lc-health-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.lc-health-meter{margin:18px 0}.lc-health-meter>div{display:flex;justify-content:space-between;gap:12px;font-size:13px}.lc-health-meter strong{font-weight:500}.lc-health-meter progress{display:block;width:100%;height:7px;margin:9px 0;accent-color:var(--dsw-alias-label-primary)}.lc-health-meter small{font-size:12px;color:var(--dsw-alias-label-secondary)}.lc-core-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.lc-core-grid>div{display:flex;align-items:center;gap:6px;font-size:10px;color:var(--dsw-alias-label-secondary)}.lc-core-grid progress{width:100%;height:5px;accent-color:var(--dsw-alias-state-success-primary)}.lc-network-row{display:grid;gap:4px;padding:12px 0;border-bottom:1px solid var(--dsw-alias-border-l1);font-size:12px}.lc-network-row strong{font-weight:500}.lc-connection{border-bottom:1px solid var(--dsw-alias-border-l1);padding-bottom:18px}.lc-connection-bar{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap}.lc-connection-bar strong{display:block;overflow-wrap:anywhere;font-size:13px;font-weight:500}.lc-connection-fields{grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:16px;margin:18px 0}.lc-connection .lc-actions{flex-wrap:wrap;margin:0}.lc-connection-setup{margin-top:18px}.lc-connection-setup h3{margin-top:20px}.lc-connection input{width:100%;min-width:0}.lc-steps{padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.9}.lc-webui-link{display:inline-flex;text-decoration:none;margin:12px 0}.lc-engine-setup{margin-bottom:24px}.lc-engine-setup .lc-card p{margin:12px 0}.lc-engine-setup summary{cursor:pointer}.lc .lc-facts>div{min-width:0}.lc .lc-facts strong{overflow-wrap:anywhere}.lc-empty h3{margin-bottom:8px}.lc-empty .lc-button{margin-top:16px}.lc button:focus-visible,.lc a:focus-visible,.lc input:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}\n@media(max-width:800px){.lc-health-grid{grid-template-columns:1fr}.lc{padding:16px}.lc-header{align-items:flex-start}.lc-connection-bar{align-items:flex-start}.lc-tabs{gap:3px}.lc-tab{padding:9px 11px}.lc-section{gap:12px;flex-wrap:wrap}.lc-header h1{font-size:23px}.lc-header .lc-mark{display:none}.lc-connection-bar .lc-actions{width:100%}.lc-network-row{overflow-wrap:anywhere}}\n.lc-ninfer-hero{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px;align-items:start}\n.lc-ninfer-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}\n.lc-ninfer-kpis{grid-template-columns:repeat(4,minmax(0,1fr))}\n.lc-pill.bad{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,var(--panel));border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 25%,var(--line))}\n@media(max-width:1150px){.lc-ninfer-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}\n@media(max-width:700px){.lc-ninfer-hero{grid-template-columns:1fr}.lc-ninfer-actions{justify-content:flex-start}}\n@media(max-width:520px){.lc-ninfer-kpis{grid-template-columns:1fr}}\n\n/* NInfer: connection details, client examples and engine facts. */\n.lc-ninfer-split{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(0,1fr);gap:16px;margin-top:16px;align-items:start}.lc-ninfer-split>*{min-width:0}\n.lc-api-rows{display:grid;gap:9px}\n.lc-api-row{display:grid;gap:6px;padding:11px 13px;border:1px solid var(--line);border-radius:10px;background:var(--dsw-alias-bg-layer-1);min-width:0}\n.lc-api-row>header{display:flex;align-items:center;justify-content:space-between;gap:10px}\n.lc-api-row>header>span{font-size:10px;letter-spacing:1.6px;text-transform:uppercase;color:var(--muted)}\n.lc-api-row code{font:12.5px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--ink);overflow-wrap:anywhere}\n.lc-api-note{margin-top:10px}\n.lc-copy{border:1px solid var(--line);background:var(--dsw-alias-bg-base);color:var(--muted);border-radius:7px;padding:4px 10px;font-size:11px;line-height:1.4;white-space:nowrap;min-width:58px}\n.lc-copy:hover{color:var(--ink);border-color:var(--ink)}\n.lc-copy.copied{color:var(--teal);border-color:color-mix(in srgb,var(--teal) 45%,var(--line))}\n.lc-example{margin-top:14px;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-1);min-width:0}\n.lc-example>header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 12px;border-bottom:1px solid var(--line);flex-wrap:wrap}\n.lc-example-tabs{display:flex;gap:2px;padding:3px;background:var(--dsw-alias-bg-layer-2);border-radius:9px}\n.lc-example-tab{border:0;background:transparent;color:var(--muted);font-size:11px;padding:5px 11px;border-radius:7px;line-height:1.4}\n.lc-example-tab.active{background:var(--dsw-alias-bg-layer-2);color:var(--ink)}\n.lc-example pre{margin:0;padding:13px 14px;max-height:230px;overflow:auto;font:11.5px/1.65 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--ink);white-space:pre}\n.lc-spec-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}\n.lc-spec{padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--dsw-alias-bg-layer-1);min-width:0}\n.lc-spec>span{display:block;font-size:10px;letter-spacing:1.6px;text-transform:uppercase;color:var(--muted)}\n.lc-spec>strong{display:block;margin-top:4px;font-size:13px;font-weight:500;overflow-wrap:anywhere}\n.lc-ninfer-kpis{gap:12px}\n.lc-ninfer-kpis .lc-card{padding:14px 15px}\n.lc-ninfer-kpis .lc-value{font-size:21px;margin:6px 0 2px}\n@media(max-width:1180px){.lc-ninfer-split{grid-template-columns:minmax(0,1fr)}}\n@media(max-width:700px){.lc-spec-grid{grid-template-columns:1fr}.lc-example pre{max-height:180px}}";
526
526
  //#endregion
527
527
  //#region src/client/Dashboard.tsx
528
528
  function Icon({ kind = "chip", size = 18 }) {
@@ -683,6 +683,232 @@ window.__ModuleLoader__.load({
683
683
  };
684
684
  }
685
685
  //#endregion
686
+ //#region src/client/NInferApi.tsx
687
+ /** Copy with the async clipboard, falling back to the selection API in a page that denies it. */
688
+ async function copyText(value) {
689
+ try {
690
+ if (navigator.clipboard?.writeText) {
691
+ await navigator.clipboard.writeText(value);
692
+ return true;
693
+ }
694
+ } catch (error) {}
695
+ try {
696
+ const area = document.createElement("textarea");
697
+ area.value = value;
698
+ area.readOnly = true;
699
+ area.style.position = "fixed";
700
+ area.style.top = "-1000px";
701
+ area.style.opacity = "0";
702
+ document.body.appendChild(area);
703
+ area.select();
704
+ const copied = document.execCommand("copy");
705
+ area.remove();
706
+ return copied;
707
+ } catch (error) {
708
+ return false;
709
+ }
710
+ }
711
+ /** One copy button that says so for a moment, so a copy is never a guess. */
712
+ function Copy({ value, what, label = "" }) {
713
+ const [copied, setCopied] = (0, react.useState)(false);
714
+ (0, react.useEffect)(() => {
715
+ if (!copied) return;
716
+ const timer = setTimeout(() => setCopied(false), 1600);
717
+ return () => clearTimeout(timer);
718
+ }, [copied]);
719
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
720
+ type: "button",
721
+ className: `lc-copy${copied ? " copied" : ""}`,
722
+ "aria-label": `Copy ${what}`,
723
+ onClick: () => void copyText(value).then((ok) => {
724
+ if (ok) setCopied(true);
725
+ }),
726
+ children: copied ? "Copied" : label || "Copy"
727
+ });
728
+ }
729
+ /** A labelled address with its own copy button. */
730
+ function Address({ label, value, what, copy = true }) {
731
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
732
+ className: "lc-api-row",
733
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), copy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Copy, {
734
+ value,
735
+ what
736
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: value })]
737
+ });
738
+ }
739
+ /** One labelled engine figure. */
740
+ function Spec({ label, value }) {
741
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
742
+ className: "lc-spec",
743
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: value })]
744
+ });
745
+ }
746
+ const examples = [
747
+ "curl",
748
+ "python",
749
+ "node"
750
+ ];
751
+ /**
752
+ * What a client needs to talk to this engine: the OpenAI-compatible address,
753
+ * the model name, the auth the engine actually asks for, and a runnable example.
754
+ */
755
+ function NInferApi({ state, running }) {
756
+ const [example, setExample] = (0, react.useState)("curl");
757
+ const base = (state?.baseUrl ?? "http://127.0.0.1:8083/v1").replace(/\/v1\/?$/u, "");
758
+ const model = state?.modelName ?? "qwen3.8-27b";
759
+ const env = `OPENAI_BASE_URL=${base}/v1\nOPENAI_API_KEY=local\nOPENAI_MODEL=${model}`;
760
+ const code = {
761
+ curl: `curl ${base}/v1/chat/completions \\\n -H 'content-type: application/json' \\\n -d '${JSON.stringify({
762
+ model,
763
+ messages: [{
764
+ role: "user",
765
+ content: "Hello"
766
+ }],
767
+ max_tokens: 128
768
+ })}'`,
769
+ python: `from openai import OpenAI\n\nclient = OpenAI(base_url="${base}/v1", api_key="local")\nreply = client.chat.completions.create(\n model="${model}",\n messages=[{"role": "user", "content": "Hello"}],\n)\nprint(reply.choices[0].message.content)`,
770
+ node: `import OpenAI from 'openai'\n\nconst client = new OpenAI({ baseURL: '${base}/v1', apiKey: 'local' })\nconst reply = await client.chat.completions.create({\n model: '${model}',\n messages: [{ role: 'user', content: 'Hello' }],\n})\nconsole.log(reply.choices[0].message.content)`
771
+ };
772
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
773
+ className: "lc-card",
774
+ "aria-label": "API and clients",
775
+ children: [
776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
777
+ className: "lc-section",
778
+ style: { marginTop: 0 },
779
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Connect your tools" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
780
+ className: "lc-sub",
781
+ children: "Any OpenAI-compatible client works with these values. The engine answers on this computer only."
782
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
783
+ className: "lc-actions",
784
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Copy, {
785
+ label: "Copy .env",
786
+ value: env,
787
+ what: "the .env settings"
788
+ })
789
+ })]
790
+ }),
791
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
792
+ className: "lc-api-rows",
793
+ children: [
794
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Address, {
795
+ label: "Base URL",
796
+ value: `${base}/v1`,
797
+ what: "the base URL"
798
+ }),
799
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Address, {
800
+ label: "Chat completions",
801
+ value: `${base}/v1/chat/completions`,
802
+ what: "the chat completions URL"
803
+ }),
804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Address, {
805
+ label: "Model name",
806
+ value: model,
807
+ what: "the model name"
808
+ }),
809
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Address, {
810
+ label: "Models endpoint",
811
+ value: `${base}/v1/models`,
812
+ what: "the models URL"
813
+ }),
814
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Address, {
815
+ label: "API key",
816
+ value: "Not required on this computer",
817
+ what: "the API key",
818
+ copy: false
819
+ })
820
+ ]
821
+ }),
822
+ !running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
823
+ className: "lc-sub lc-api-note",
824
+ children: "NInfer is not serving right now, so these addresses answer after Start NInfer."
825
+ }),
826
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
827
+ className: "lc-example",
828
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
829
+ className: "lc-example-tabs",
830
+ role: "tablist",
831
+ "aria-label": "Client example",
832
+ children: examples.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
833
+ type: "button",
834
+ role: "tab",
835
+ "aria-selected": example === name,
836
+ className: `lc-example-tab${example === name ? " active" : ""}`,
837
+ onClick: () => setExample(name),
838
+ children: name === "curl" ? "curl" : name === "python" ? "Python" : "Node.js"
839
+ }, name))
840
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Copy, {
841
+ value: code[example],
842
+ what: "the client example"
843
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: code[example] })]
844
+ })
845
+ ]
846
+ });
847
+ }
848
+ /** The pinned engine this installation serves, as the host reports it. */
849
+ function NInferSpec({ state }) {
850
+ const runtime = state?.runtime === "wsl" ? `WSL · pid ${state.pid ?? "—"}` : state?.containerId ? `Container · ${state.containerId.slice(0, 12)}` : state?.pid ? `Process · pid ${state.pid}` : "Not running";
851
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
852
+ className: "lc-card",
853
+ "aria-label": "Engine configuration",
854
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
855
+ className: "lc-section",
856
+ style: { marginTop: 0 },
857
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Engine" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
858
+ className: "lc-sub",
859
+ children: "The pinned artifact this page starts and serves."
860
+ })] }), state?.complete === true && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
861
+ className: "lc-pill good",
862
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: "lc-dot" }), "verified"]
863
+ })]
864
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
865
+ className: "lc-spec-grid",
866
+ children: [
867
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
868
+ label: "Model",
869
+ value: state?.modelName ?? "qwen3.8-27b"
870
+ }),
871
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
872
+ label: "Runtime",
873
+ value: runtime
874
+ }),
875
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
876
+ label: "Context",
877
+ value: state?.context == null ? "—" : state.context.toLocaleString() + " tokens"
878
+ }),
879
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
880
+ label: "Response limit",
881
+ value: state?.outputTokens == null ? "—" : state.outputTokens.toLocaleString() + " tokens"
882
+ }),
883
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
884
+ label: "Prefill chunk",
885
+ value: state?.prefill == null ? "—" : state.prefill.toLocaleString()
886
+ }),
887
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
888
+ label: "Host KV",
889
+ value: state?.hostKvMiB == null ? "—" : state.hostKvMiB.toLocaleString() + " MiB"
890
+ }),
891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
892
+ label: "MTP drafts",
893
+ value: state?.draftTokens == null ? "—" : String(state.draftTokens)
894
+ }),
895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
896
+ label: "Vision",
897
+ value: state?.vision ? "Enabled" : "Text only"
898
+ }),
899
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
900
+ label: "n-gram drafts",
901
+ value: state?.ngram ?? "chain"
902
+ }),
903
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spec, {
904
+ label: "Access",
905
+ value: "127.0.0.1 only"
906
+ })
907
+ ]
908
+ })]
909
+ });
910
+ }
911
+ //#endregion
686
912
  //#region src/client/NInferPanel.tsx
687
913
  const gb = (mib) => mib == null ? "—" : (mib / 1024).toFixed(1);
688
914
  const rate = (value) => value == null ? "—" : value >= 1e3 ? (value / 1e3).toFixed(2) + "k" : value.toFixed(1);
@@ -795,6 +1021,10 @@ window.__ModuleLoader__.load({
795
1021
  const endpoint = state?.baseUrl ?? "http://127.0.0.1:8083/v1";
796
1022
  const dashboard = endpoint.replace(/\/v1\/?$/u, "") + "/";
797
1023
  const canStart = phase === "stopped" && state?.complete === true;
1024
+ /** Another launcher may hold the engine; when it is provably ours the page can reclaim it. */
1025
+ const external = phase === "unmanaged";
1026
+ const canTakeOver = external && state?.takeover === true;
1027
+ const canStop = state?.owned === true || canTakeOver;
798
1028
  const progress = loading ? Math.max(0, Math.min(100, live.summary.weightPercent ?? 0)) : 0;
799
1029
  const sampleCount = live.history.filter((sample) => sample.decode != null).length;
800
1030
  const vramReading = vramUsed != null && vramTotal != null;
@@ -846,13 +1076,24 @@ window.__ModuleLoader__.load({
846
1076
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
847
1077
  className: "lc-actions",
848
1078
  style: { marginTop: 14 },
849
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
850
- className: `lc-pill ${pill}`,
851
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: "lc-dot" }), label]
852
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
853
- className: "lc-pill",
854
- children: endpoint
855
- })]
1079
+ children: [
1080
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1081
+ className: `lc-pill ${pill}`,
1082
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: "lc-dot" }), label]
1083
+ }),
1084
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1085
+ className: "lc-pill",
1086
+ children: endpoint
1087
+ }),
1088
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1089
+ className: "lc-pill",
1090
+ children: `MTP ${state?.draftTokens ?? 3}`
1091
+ }),
1092
+ state?.vision === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1093
+ className: "lc-pill",
1094
+ children: "vision"
1095
+ })
1096
+ ]
856
1097
  })
857
1098
  ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
858
1099
  className: "lc-ninfer-actions",
@@ -865,13 +1106,13 @@ window.__ModuleLoader__.load({
865
1106
  }),
866
1107
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
867
1108
  className: "lc-button",
868
- disabled: busy || state?.owned !== true,
1109
+ disabled: busy || !canStop,
869
1110
  onClick: () => void act("stop"),
870
1111
  children: "Stop NInfer"
871
1112
  }),
872
1113
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
873
1114
  className: "lc-button",
874
- disabled: busy || state?.owned !== true,
1115
+ disabled: busy || !canStop,
875
1116
  onClick: () => void act("restart"),
876
1117
  children: "Restart NInfer"
877
1118
  }),
@@ -884,7 +1125,7 @@ window.__ModuleLoader__.load({
884
1125
  },
885
1126
  children: "Refresh"
886
1127
  }),
887
- running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1128
+ (running || external && state?.healthy === true) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
888
1129
  className: "lc-button",
889
1130
  href: dashboard,
890
1131
  target: "_blank",
@@ -910,6 +1151,38 @@ window.__ModuleLoader__.load({
910
1151
  role: "status",
911
1152
  children: notice
912
1153
  }),
1154
+ external && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1155
+ className: "lc-notice",
1156
+ role: "status",
1157
+ children: [
1158
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "NInfer is running outside this Control Center." }),
1159
+ " ",
1160
+ state?.detail,
1161
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1162
+ className: "lc-actions",
1163
+ children: [
1164
+ canTakeOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1165
+ className: "lc-button primary",
1166
+ disabled: busy,
1167
+ onClick: () => void act("start"),
1168
+ children: "Take over engine"
1169
+ }),
1170
+ canTakeOver && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1171
+ className: "lc-button",
1172
+ disabled: busy,
1173
+ onClick: () => void act("stop"),
1174
+ children: "Stop the external engine"
1175
+ }),
1176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1177
+ className: "lc-button ghost",
1178
+ disabled: busy,
1179
+ onClick: () => refresh(),
1180
+ children: "Refresh status"
1181
+ })
1182
+ ]
1183
+ })
1184
+ ]
1185
+ }),
913
1186
  failed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
914
1187
  className: "lc-notice error",
915
1188
  role: "alert",
@@ -1001,55 +1274,72 @@ window.__ModuleLoader__.load({
1001
1274
  ]
1002
1275
  }),
1003
1276
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1004
- className: "lc-grid lc-ninfer-kpis",
1277
+ className: "lc-ninfer-split",
1278
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NInferApi, {
1279
+ state,
1280
+ running
1281
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NInferSpec, { state })]
1282
+ }),
1283
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1284
+ className: "lc-card",
1005
1285
  style: { marginTop: 16 },
1006
- children: [
1007
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1008
- label: "MODEL STATE",
1009
- value: label,
1010
- detail: running ? (live.summary.running ?? 0) > 0 ? `${live.summary.running} in flight` : "Waiting for a request" : loading ? "Reading weights" : failed ? "Startup failed" : "Not loaded"
1011
- }),
1012
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1013
- label: "DECODE",
1014
- value: running ? rate(live.summary.decodeTokPerSecond) : "—",
1015
- detail: "prefill " + rate(live.summary.prefillTokPerSecond) + " tok/s"
1016
- }),
1017
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1018
- label: "MTP ACCEPTANCE",
1019
- value: live.summary.mtpPercent == null ? "—" : live.summary.mtpPercent.toFixed(1) + "%",
1020
- detail: live.summary.mtpOffered ? `${live.summary.mtpAccepted?.toLocaleString()} / ${live.summary.mtpOffered.toLocaleString()} accepted` : "No speculative request yet"
1021
- }),
1022
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1023
- label: "VRAM",
1024
- value: `${gb(vramUsed)} / ${gb(vramTotal)} GB`,
1025
- detail: gpu?.name ?? "No GPU reading",
1026
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1027
- className: "lc-bar",
1028
- style: { margin: "10px 0 4px" },
1029
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: (vramPercent ?? 0) + "%" } })
1286
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1287
+ className: "lc-section",
1288
+ style: { marginTop: 0 },
1289
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "Live engine" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1290
+ className: "lc-sub",
1291
+ children: "From the engine's own log and this computer's sensors."
1292
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "3 s refresh" })]
1293
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1294
+ className: "lc-grid lc-ninfer-kpis",
1295
+ children: [
1296
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1297
+ label: "MODEL STATE",
1298
+ value: label,
1299
+ detail: running ? (live.summary.running ?? 0) > 0 ? `${live.summary.running} in flight` : "Waiting for a request" : loading ? "Reading weights" : failed ? "Startup failed" : "Not loaded"
1300
+ }),
1301
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1302
+ label: "DECODE",
1303
+ value: running ? rate(live.summary.decodeTokPerSecond) : "—",
1304
+ detail: "prefill " + rate(live.summary.prefillTokPerSecond) + " tok/s"
1305
+ }),
1306
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1307
+ label: "MTP ACCEPTANCE",
1308
+ value: live.summary.mtpPercent == null ? "—" : live.summary.mtpPercent.toFixed(1) + "%",
1309
+ detail: live.summary.mtpOffered ? `${live.summary.mtpAccepted?.toLocaleString()} / ${live.summary.mtpOffered.toLocaleString()} accepted` : "No speculative request yet"
1310
+ }),
1311
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1312
+ label: "VRAM",
1313
+ value: `${gb(vramUsed)} / ${gb(vramTotal)} GB`,
1314
+ detail: gpu?.name ?? "No GPU reading",
1315
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1316
+ className: "lc-bar",
1317
+ style: { margin: "10px 0 4px" },
1318
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: (vramPercent ?? 0) + "%" } })
1319
+ })
1320
+ }),
1321
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1322
+ label: "GPU LOAD",
1323
+ value: gpu?.utilization == null ? "—" : Math.round(gpu.utilization) + "%",
1324
+ detail: gpu?.temperature == null ? "No temperature" : `${Math.round(gpu.temperature)} °C${gpu.powerWatts == null ? "" : " · " + Math.round(gpu.powerWatts) + " W"}`
1325
+ }),
1326
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1327
+ label: "CONTEXT",
1328
+ value: context,
1329
+ detail: `${state?.vision ? "vision" : "text only"} · MTP ${state?.draftTokens ?? 3}`
1330
+ }),
1331
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1332
+ label: "ACTIVE REQUESTS",
1333
+ value: running ? String(live.summary.running ?? 0) : "—",
1334
+ detail: `${live.summary.requests?.toLocaleString() ?? 0} requests served`
1335
+ }),
1336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1337
+ label: "HOST",
1338
+ value: live.telemetry?.cpuPct == null ? "—" : Math.round(live.telemetry.cpuPct) + "%",
1339
+ detail: live.telemetry?.ramTotalGiB ? `CPU · RAM ${live.telemetry.ramUsedGiB?.toFixed(1)} / ${live.telemetry.ramTotalGiB.toFixed(1)} GiB` : "CPU · no memory reading"
1030
1340
  })
1031
- }),
1032
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1033
- label: "GPU LOAD",
1034
- value: gpu?.utilization == null ? "—" : Math.round(gpu.utilization) + "%",
1035
- detail: gpu?.temperature == null ? "No temperature" : `${Math.round(gpu.temperature)} °C${gpu.powerWatts == null ? "" : " · " + Math.round(gpu.powerWatts) + " W"}`
1036
- }),
1037
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1038
- label: "CONTEXT",
1039
- value: context,
1040
- detail: `${state?.vision ? "vision" : "text only"} · MTP ${state?.draftTokens ?? 3}`
1041
- }),
1042
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1043
- label: "ACTIVE REQUESTS",
1044
- value: running ? String(live.summary.running ?? 0) : "—",
1045
- detail: `${live.summary.requests?.toLocaleString() ?? 0} requests served`
1046
- }),
1047
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Kpi, {
1048
- label: "HOST",
1049
- value: live.telemetry?.cpuPct == null ? "—" : Math.round(live.telemetry.cpuPct) + "%",
1050
- detail: live.telemetry?.ramTotalGiB ? `CPU · RAM ${live.telemetry.ramUsedGiB?.toFixed(1)} / ${live.telemetry.ramTotalGiB.toFixed(1)} GiB` : "CPU · no memory reading"
1051
- })
1052
- ]
1341
+ ]
1342
+ })]
1053
1343
  }),
1054
1344
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1055
1345
  className: "lc-card",