} Resolves to true if successful, false if aborted/error.
*/
-async function goto(targetX, targetY, mode = 'direct') {
+async function goto(targetX, targetY, mode = 'direct', isIdleMove = false) {
if (isMoving) {
- UI.log('Arm is currently moving! Wait for it to finish.', 'warn');
- return false;
+ // If the currently executing move is an idle move, and this is a user move, we can interrupt it.
+ if (currentIsIdleMove && !isIdleMove) {
+ stopCurrentMove(); // Abort the idle move
+ } else {
+ UI.log('Arm is currently moving! Wait for it to finish.', 'warn');
+ return false;
+ }
}
+ currentIsIdleMove = isIdleMove;
mode = MotionEffects.resolveMode(mode);
currentMotionMode = mode;
@@ -283,6 +301,8 @@ async function goto(targetX, targetY, mode = 'direct') {
await MotionEffects.onSequenceStart(mode);
+ document.dispatchEvent(new CustomEvent('wiji:motion-start', { detail: { isIdleMove: currentIsIdleMove } }));
+
return new Promise((resolve) => {
currentResolve = resolve;
executeQueue();
diff --git a/web/js/sections/board-control.js b/web/js/sections/board-control.js
index ea0f990..7c12057 100644
--- a/web/js/sections/board-control.js
+++ b/web/js/sections/board-control.js
@@ -4,6 +4,7 @@ import IK from '../kinematics.js';
import Settings from '../settings.js';
import Motion from '../motion.js';
import { buildQuickSequencesHTML, initQuickSequences } from '../quick-sequences.js';
+import { IdleEffects } from '../idle-effects.js';
let eventCleanup = [];
let isHomed = false;
@@ -161,6 +162,16 @@ function buildHTML() {
+
+
+
+
+
+
+
+
+
+
@@ -266,6 +277,7 @@ async function loadSpots() {
if (!parsedSpots.find(s => s.id === 'PARK')) {
parsedSpots.push({ id: 'PARK', label: 'Park Position', x: parkPosition.x, y: parkPosition.y });
}
+ IdleEffects.setParkPosition(parkPosition.x, parkPosition.y);
spots = parsedSpots;
document.getElementById('bg-image').src = `web/assets/backgrounds/${currentBg}/bg.svg?v=${Date.now()}`;
@@ -355,6 +367,15 @@ export default {
updateHomingUI();
loadSpots();
+ // ── Idle Effects Selection ──────────────────────────────────
+ const idleSelect = document.getElementById('it-idle-mode-select');
+ if (idleSelect) {
+ idleSelect.value = IdleEffects.getMode();
+ idleSelect.addEventListener('change', () => {
+ IdleEffects.setMode(idleSelect.value);
+ });
+ }
+
// ── Background Selection ──────────────────────────────────
const bgSelect = document.getElementById('bg-select');
if (bgSelect) {
diff --git a/web/js/sections/sequence-editor.js b/web/js/sections/sequence-editor.js
index a1ac1c3..e9a2555 100644
--- a/web/js/sections/sequence-editor.js
+++ b/web/js/sections/sequence-editor.js
@@ -4,6 +4,7 @@ import IK from '../kinematics.js';
import Settings from '../settings.js';
import SequenceRunner from '../sequence-runner.js';
import Motion from '../motion.js';
+import { IdleEffects } from '../idle-effects.js';
let eventCleanup = [];
let isHomed = false;
@@ -282,6 +283,16 @@ function buildHTML() {
+
+
+
+
+
@@ -432,6 +443,7 @@ async function loadSpots() {
if (!parsedSpots.find(s => s.id === 'PARK')) {
parsedSpots.push({ id: 'PARK', label: 'Park Position', x: parkPosition.x, y: parkPosition.y });
}
+ IdleEffects.setParkPosition(parkPosition.x, parkPosition.y);
spots = parsedSpots;
document.getElementById('text-input-row').style.display = hasAlphabet ? 'grid' : 'none';
@@ -578,6 +590,15 @@ export default {
});
}
+ // ── Idle Effects Selection ──────────────────────────────────
+ const idleSelect = document.getElementById('se-idle-mode-select');
+ if (idleSelect) {
+ idleSelect.value = IdleEffects.getMode();
+ idleSelect.addEventListener('change', () => {
+ IdleEffects.setMode(idleSelect.value);
+ });
+ }
+
// ── Settings Sliders ──────────────────────────────────────
const sSpeed = document.getElementById('slider-speed');
const sAccel = document.getElementById('slider-accel');