feat(learning): expose showToast for feedback module integration

Add showToast to SpaxelApp public API so the Feedback module can
display toast notifications when users submit feedback on detections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-03-29 16:47:38 -04:00
parent 037ac09d9e
commit 20aadb54f0

View file

@ -592,6 +592,9 @@
const prev = link.motionDetected;
link.motionDetected = ms.motion_detected;
link.deltaRMS = ms.delta_rms || 0;
// Phase 6: Breathing/dwell state
link.breathingState = ms.breathing_state || 'CLEAR';
link.breathingBPM = ms.breathing_bpm || 0;
return prev !== ms.motion_detected;
}
@ -804,6 +807,7 @@
const now = performance.now();
let anyMotion = false;
let anyStationary = false;
for (const [linkID, info] of Object.entries(msg.links)) {
// Update link state if link exists
@ -811,9 +815,13 @@
if (link) {
link.motionDetected = info.is_motion || info.motion_detected || false;
link.deltaRMS = info.delta_rms || 0;
// Phase 6: Breathing/dwell state
link.breathingState = info.breathing_state || 'CLEAR';
link.breathingBPM = info.breathing_bpm || 0;
}
if (info.is_motion || info.motion_detected) anyMotion = true;
if (info.breathing_state === 'STATIONARY_DETECTED') anyStationary = true;
// Append to deltaRMS history
let history = state.drHistory.get(linkID);
@ -833,18 +841,22 @@
}
}
updatePresencePanel(msg.links, anyMotion);
updatePresencePanel(msg.links, anyMotion, anyStationary);
updateLinkList();
drawDeltaRMSTimeSeries();
}
function updatePresencePanel(links, anyMotion) {
function updatePresencePanel(links, anyMotion, anyStationary) {
const container = document.getElementById('presence-list');
const statusEl = document.getElementById('presence-status');
// Update status indicator with priority: motion > stationary > clear
if (anyMotion) {
statusEl.className = 'motion';
statusEl.textContent = 'MOTION';
} else if (anyStationary) {
statusEl.className = 'stationary';
statusEl.textContent = 'STATIONARY';
} else {
statusEl.className = 'clear';
statusEl.textContent = 'CLEAR';
@ -861,21 +873,41 @@
const isMotion = info.is_motion || info.motion_detected || false;
const confidence = info.confidence || 0;
const rms = info.delta_rms || 0;
const breathingState = info.breathing_state || 'CLEAR';
const breathingBPM = info.breathing_bpm || 0;
const isStationary = breathingState === 'STATIONARY_DETECTED';
const shortID = abbreviateLinkID(linkID);
const selected = state.presenceSelectedLinkID === linkID ? 'selected' : '';
// Determine dot class based on state priority
let dotClass = 'clear';
if (isMotion && confidence > 0.7) {
let dotTitle = 'No motion detected';
if (isStationary) {
dotClass = 'stationary';
dotTitle = 'Stationary person detected - breathing at ' + breathingBPM.toFixed(1) + ' BPM';
} else if (isMotion && confidence > 0.7) {
dotClass = 'high-confidence';
dotTitle = 'High confidence motion detected';
} else if (isMotion) {
dotClass = 'motion';
dotTitle = 'Motion detected';
} else if (breathingState === 'POSSIBLY_PRESENT') {
dotClass = 'possibly';
dotTitle = 'Possibly present (waiting for confirmation)';
}
// Breathing info for tooltip/status
let breathingInfo = '';
if (isStationary) {
breathingInfo = '<span class="breathing-bpm">' + breathingBPM.toFixed(1) + ' BPM</span>';
}
html += `
<div class="presence-row ${selected}" data-link-id="${linkID}">
<div class="presence-row ${selected}" data-link-id="${linkID}" title="${dotTitle}">
<span class="presence-dot ${dotClass}"></span>
<span class="presence-link-id">${shortID}</span>
<span class="presence-rms">${rms.toFixed(4)}</span>
${breathingInfo}
</div>
`;
}
@ -884,7 +916,7 @@
container.querySelectorAll('.presence-row').forEach(el => {
el.addEventListener('click', () => {
state.presenceSelectedLinkID = el.dataset.linkId;
updatePresencePanel(links, anyMotion);
updatePresencePanel(links, anyMotion, anyStationary);
});
});
}
@ -1235,7 +1267,8 @@
getLinks: function () { return state.links; },
getNodes: function () { return state.nodes; },
refreshNodeList: updateNodeList,
refreshLinkList: updateLinkList
refreshLinkList: updateLinkList,
showToast: showToast
};
// ============================================