update frontend components and cargo config

This commit is contained in:
lenn
2026-05-20 10:18:27 +08:00
parent c579544351
commit b581e310ed
3 changed files with 116 additions and 12 deletions

View File

@@ -29,6 +29,7 @@
export let summary: HudSummary; export let summary: HudSummary;
export let pressureMatrix: number[] | null = null; export let pressureMatrix: number[] | null = null;
export let spatialForce: HudSpatialForce | null = null; export let spatialForce: HudSpatialForce | null = null;
export let devkitSpatialForce: HudSpatialForce | null = null;
export let showConfigPanel = false; export let showConfigPanel = false;
export let configPanelTitle = ""; export let configPanelTitle = "";
export let configPanelHint = ""; export let configPanelHint = "";
@@ -327,6 +328,29 @@
{locale} {locale}
side="right" side="right"
panelIndex={rightPanels.length} panelIndex={rightPanels.length}
panelCode="ALG"
panelTitle={locale === "zh-CN" ? "本地切向力" : "Local Tangential"}
badgeLabel={locale === "zh-CN" ? "算法" : "ALGO"}
/>
</div>
<div
class="panel-motion-shell"
in:fly={{ x: 180, duration: 340, opacity: 0.08, easing: cubicOut }}
out:fly={{ x: 180, duration: 280, opacity: 0.06, easing: cubicIn }}
>
<SpatialForcePanel
spatialForce={devkitSpatialForce}
{locale}
side="right"
panelIndex={rightPanels.length + 1}
panelCode="DKT"
panelTitle={locale === "zh-CN" ? "DevKit 切向力" : "DevKit Tangential"}
badgeLabel="DEVKIT"
badgeTone="lime"
showMetrics={false}
requireMagnitude={false}
compactMetaText={locale === "zh-CN" ? "等待 DevKit 角度流" : "Waiting for DevKit angle"}
/> />
</div> </div>

View File

@@ -5,6 +5,13 @@
export let side: "left" | "right" = "right"; export let side: "left" | "right" = "right";
export let panelIndex = 0; export let panelIndex = 0;
export let locale: "zh-CN" | "en-US" = "zh-CN"; export let locale: "zh-CN" | "en-US" = "zh-CN";
export let panelCode = "TAN";
export let panelTitle = "";
export let badgeLabel = "";
export let badgeTone: "cyan" | "lime" | "orange" = "cyan";
export let showMetrics = true;
export let requireMagnitude = true;
export let compactMetaText = "";
function formatValue(value: number | null, digits = 1): string { function formatValue(value: number | null, digits = 1): string {
if (value === null || !Number.isFinite(value)) { if (value === null || !Number.isFinite(value)) {
@@ -91,11 +98,15 @@
strength: "Strength", strength: "Strength",
confidence: "Confidence" confidence: "Confidence"
}; };
$: resolvedTitle = panelTitle || i18n.title;
$: resolvedBadgeLabel = badgeLabel || i18n.angle;
$: resolvedCompactMetaText =
compactMetaText || (locale === "zh-CN" ? "仅使用角度流" : "Angle stream only");
$: hasData = $: hasData =
spatialForce !== null && spatialForce !== null &&
Number.isFinite(spatialForce.angleDeg) && Number.isFinite(spatialForce.angleDeg) &&
Number.isFinite(spatialForce.magnitude); (!requireMagnitude || Number.isFinite(spatialForce.magnitude));
$: angleDeg = hasData ? normalizeAngle(spatialForce?.angleDeg ?? 0) : 0; $: angleDeg = hasData ? normalizeAngle(spatialForce?.angleDeg ?? 0) : 0;
$: updateVisualAngle(angleDeg, hasData); $: updateVisualAngle(angleDeg, hasData);
$: magnitude = hasData ? spatialForce?.magnitude ?? 0 : null; $: magnitude = hasData ? spatialForce?.magnitude ?? 0 : null;
@@ -110,12 +121,12 @@
> >
<header class="panel-head"> <header class="panel-head">
<div class="head-text"> <div class="head-text">
<p class="panel-code">TAN</p> <p class="panel-code">{panelCode}</p>
<p class="panel-title">{i18n.title}</p> <p class="panel-title">{resolvedTitle}</p>
</div> </div>
<div class="icon-layer" aria-hidden="true"> <div class="icon-layer" aria-hidden="true">
<span class="icon-chip tone-cyan">{i18n.angle}</span> <span class={`icon-chip tone-${badgeTone}`}>{resolvedBadgeLabel}</span>
</div> </div>
</header> </header>
@@ -152,8 +163,13 @@
<div class="angle-stage"> <div class="angle-stage">
<p class="angle-label">{i18n.heading}</p> <p class="angle-label">{i18n.heading}</p>
{#if showMetrics}
<p class="angle-meta">{i18n.strength}: {formatValue(magnitude, 2)}</p> <p class="angle-meta">{i18n.strength}: {formatValue(magnitude, 2)}</p>
<p class="angle-meta">{i18n.confidence}: {hasData ? `${formatValue(confidence, 0)}%` : "--"}</p> <p class="angle-meta">{i18n.confidence}: {hasData ? `${formatValue(confidence, 0)}%` : "--"}</p>
{:else}
<p class="angle-meta">{resolvedCompactMetaText}</p>
<p class="angle-meta">{hasData ? (locale === "zh-CN" ? "实时对比中" : "Live comparison") : "--"}</p>
{/if}
</div> </div>
</div> </div>
</article> </article>
@@ -251,6 +267,14 @@
border-color: rgb(var(--hud-cyan-rgb) / 0.54); border-color: rgb(var(--hud-cyan-rgb) / 0.54);
} }
.icon-chip.tone-lime {
border-color: rgb(var(--hud-lime-rgb) / 0.54);
}
.icon-chip.tone-orange {
border-color: rgb(var(--hud-orange-rgb) / 0.54);
}
.panel-body { .panel-body {
display: grid; display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(10rem, 0.9fr); grid-template-columns: minmax(0, 1.1fr) minmax(10rem, 0.9fr);

View File

@@ -46,6 +46,13 @@
dtsMs: number; dtsMs: number;
} }
interface DevKitPztAngleEvent {
seq: number;
timestampMs: number;
dtsMs: number;
angle: number;
}
const copyByLocale: Record<LocaleCode, HudCopy> = { const copyByLocale: Record<LocaleCode, HudCopy> = {
"zh-CN": { "zh-CN": {
appName: "JE-Skin", appName: "JE-Skin",
@@ -230,6 +237,7 @@
let summary: HudSummary = buildEmptySummary(); let summary: HudSummary = buildEmptySummary();
let pressureMatrix: number[] | null = null; let pressureMatrix: number[] | null = null;
let spatialForce: HudSpatialForce | null = null; let spatialForce: HudSpatialForce | null = null;
let devkitSpatialForce: HudSpatialForce | null = null;
let matrixRows = 12; let matrixRows = 12;
let matrixCols = 7; let matrixCols = 7;
let rangeMin = DEFAULT_PRESSURE_RANGE_MIN; let rangeMin = DEFAULT_PRESSURE_RANGE_MIN;
@@ -270,6 +278,7 @@
rowsKept: number; rowsKept: number;
} | null = null; } | null = null;
let devkitStatusTimer: number | null = null; let devkitStatusTimer: number | null = null;
let devkitSpatialForceClearTimer: number | null = null;
let sessionStartedAt: number = Date.now(); let sessionStartedAt: number = Date.now();
$: uiCopy = copyByLocale[locale]; $: uiCopy = copyByLocale[locale];
@@ -297,6 +306,31 @@
return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window; return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
} }
function clearDevkitSpatialForce(): void {
devkitSpatialForce = null;
if (devkitSpatialForceClearTimer != null && typeof window !== "undefined") {
window.clearTimeout(devkitSpatialForceClearTimer);
devkitSpatialForceClearTimer = null;
}
hasSignalData = signalPanels.length > 0 || summary.points.length > 0 || spatialForce !== null;
}
function scheduleDevkitSpatialForceClear(): void {
if (typeof window === "undefined") {
return;
}
if (devkitSpatialForceClearTimer != null) {
window.clearTimeout(devkitSpatialForceClearTimer);
}
devkitSpatialForceClearTimer = window.setTimeout(() => {
devkitSpatialForce = null;
devkitSpatialForceClearTimer = null;
hasSignalData = signalPanels.length > 0 || summary.points.length > 0 || spatialForce !== null;
}, 420);
}
function clamp(value: number, min: number, max: number): number { function clamp(value: number, min: number, max: number): number {
return Math.min(max, Math.max(min, value)); return Math.min(max, Math.max(min, value));
} }
@@ -720,6 +754,7 @@
function resetReplayVisualState(): void { function resetReplayVisualState(): void {
pressureMatrix = buildZeroMatrix(); pressureMatrix = buildZeroMatrix();
spatialForce = null; spatialForce = null;
clearDevkitSpatialForce();
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
summary = buildEmptySummary(); summary = buildEmptySummary();
hasSignalData = false; hasSignalData = false;
@@ -756,6 +791,7 @@
replayProgress = replayFrames.length > 1 ? safeIndex / (replayFrames.length - 1) : 1; replayProgress = replayFrames.length > 1 ? safeIndex / (replayFrames.length - 1) : 1;
pressureMatrix = frameValuesToMatrix(replayFrames[safeIndex].values); pressureMatrix = frameValuesToMatrix(replayFrames[safeIndex].values);
spatialForce = null; spatialForce = null;
clearDevkitSpatialForce();
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
summary = buildReplaySummaryAt(safeIndex); summary = buildReplaySummaryAt(safeIndex);
hasSignalData = true; hasSignalData = true;
@@ -1011,7 +1047,11 @@
} }
pressureMatrix = packet.pressureMatrix; pressureMatrix = packet.pressureMatrix;
spatialForce = packet.spatialForce ?? null; spatialForce = packet.spatialForce ?? null;
hasSignalData = signalPanels.length > 0 || packet.summary.points.length > 0 || spatialForce !== null; hasSignalData =
signalPanels.length > 0 ||
packet.summary.points.length > 0 ||
spatialForce !== null ||
devkitSpatialForce !== null;
} }
function clearHudPanels(): void { function clearHudPanels(): void {
@@ -1020,6 +1060,7 @@
summary = buildEmptySummary(); summary = buildEmptySummary();
pressureMatrix = null; pressureMatrix = null;
spatialForce = null; spatialForce = null;
clearDevkitSpatialForce();
} }
function startMockFeed(push: (packet: HudPacket) => void): () => void { function startMockFeed(push: (packet: HudPacket) => void): () => void {
@@ -1794,12 +1835,25 @@
.catch((error) => { .catch((error) => {
console.error("Failed to listen for hud_stream:", error); console.error("Failed to listen for hud_stream:", error);
}); });
void listen<{ seq: number; timestampMs: number; dtsMs: number; angle: number }>( void listen<DevKitPztAngleEvent>("devkit_pzt_angle", (event) => {
"devkit_pzt_angle", const angleDeg = Number(event.payload.angle);
(event) => { if (!Number.isFinite(angleDeg)) {
console.log("[devkit_pzt_angle]", event.payload); clearDevkitSpatialForce();
return;
} }
)
devkitSpatialForce = {
angleDeg,
magnitude: 0,
confidence: 0
};
scheduleDevkitSpatialForceClear();
hasSignalData =
signalPanels.length > 0 ||
summary.points.length > 0 ||
spatialForce !== null ||
devkitSpatialForce !== null;
})
.then((unlisten) => { .then((unlisten) => {
if (disposed) { if (disposed) {
unlisten(); unlisten();
@@ -1818,6 +1872,7 @@
return () => { return () => {
disposed = true; disposed = true;
pauseReplayPlayback(); pauseReplayPlayback();
clearDevkitSpatialForce();
stopMockFeed?.(); stopMockFeed?.();
unlistenHudStream?.(); unlistenHudStream?.();
unlistenDevkitPztAngle?.(); unlistenDevkitPztAngle?.();
@@ -1937,6 +1992,7 @@
rightPanels={rightSignalPanels} rightPanels={rightSignalPanels}
{pressureMatrix} {pressureMatrix}
{spatialForce} {spatialForce}
{devkitSpatialForce}
showConfigPanel={isConfigPanelOpen} showConfigPanel={isConfigPanelOpen}
showPrecisionTestPanel={isPrecisionTestOpen} showPrecisionTestPanel={isPrecisionTestOpen}
{summary} {summary}