Stabilize playback state and cursor feedback
This commit is contained in:
@@ -43,6 +43,7 @@ class UIInputHandlerModule extends BaseModule {
|
||||
'installMouseCursors',
|
||||
'startMouseCursorAnimation',
|
||||
'stopMouseCursorAnimation',
|
||||
'normalizeProcessState',
|
||||
'clearHistory'
|
||||
]);
|
||||
|
||||
@@ -264,12 +265,14 @@ class UIInputHandlerModule extends BaseModule {
|
||||
setProcessState(state, detail = {}) {
|
||||
const knownStates = [
|
||||
'ready',
|
||||
'paused',
|
||||
'command-waiting',
|
||||
'waiting-generating',
|
||||
'playing-generating',
|
||||
'playing-ready'
|
||||
];
|
||||
const nextState = knownStates.includes(state) ? state : 'ready';
|
||||
const requestedState = knownStates.includes(state) ? state : 'ready';
|
||||
const nextState = this.normalizeProcessState(requestedState);
|
||||
|
||||
this.applyMouseCursor(nextState);
|
||||
|
||||
@@ -302,6 +305,21 @@ class UIInputHandlerModule extends BaseModule {
|
||||
}
|
||||
}
|
||||
|
||||
normalizeProcessState(state) {
|
||||
const playbackCoordinator = this.getModule('playback-coordinator');
|
||||
const isPlaying = Boolean(playbackCoordinator?.isPlaying);
|
||||
|
||||
if (isPlaying && state === 'ready') {
|
||||
return 'playing-ready';
|
||||
}
|
||||
|
||||
if (isPlaying && state === 'waiting-generating') {
|
||||
return 'playing-generating';
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
applyTextInputAttributes(playerInput) {
|
||||
if (!playerInput) return;
|
||||
|
||||
@@ -358,13 +376,15 @@ class UIInputHandlerModule extends BaseModule {
|
||||
}
|
||||
|
||||
getMouseCursor(state) {
|
||||
if (state === 'ready') {
|
||||
if (state === 'ready' || state === 'paused') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fallback = state === 'command-waiting' || state === 'waiting-generating' ? 'progress' : 'default';
|
||||
const usesArrowBase = state === 'command-waiting' || state === 'waiting-generating';
|
||||
return this.buildMouseCursor(state, fallback, usesArrowBase ? 4 : 5, usesArrowBase ? 3 : 24, this.cursorAnimationFrame);
|
||||
if (state === 'command-waiting' || state === 'waiting-generating') {
|
||||
return this.buildMouseCursor(state, 'progress', 16, 16, this.cursorAnimationFrame);
|
||||
}
|
||||
|
||||
return this.buildMouseCursor(state, 'default', 5, 24, this.cursorAnimationFrame);
|
||||
}
|
||||
|
||||
buildMouseCursor(state, fallback = 'default', hotspotX = 12, hotspotY = 12, frame = 0) {
|
||||
@@ -401,27 +421,30 @@ class UIInputHandlerModule extends BaseModule {
|
||||
getMouseCursorSvg(state, frame = 0) {
|
||||
const stroke = '#2a1b10';
|
||||
const common = `xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none" stroke="${stroke}" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"`;
|
||||
const spinnerSpokes = Array.from({ length: 8 }, (_, index) => {
|
||||
const makeSpinner = (centerX, centerY, innerRadius, outerRadius, strokeWidth = 1.8) => Array.from({ length: 8 }, (_, index) => {
|
||||
const opacity = 0.25 + (((index + frame) % 8) / 7) * 0.75;
|
||||
const angle = (index * 45) * Math.PI / 180;
|
||||
const x1 = 24 + Math.cos(angle) * 3;
|
||||
const y1 = 24 + Math.sin(angle) * 3;
|
||||
const x2 = 24 + Math.cos(angle) * 5;
|
||||
const y2 = 24 + Math.sin(angle) * 5;
|
||||
return `<path opacity="${opacity.toFixed(2)}" d="M${x1.toFixed(2)} ${y1.toFixed(2)} ${x2.toFixed(2)} ${y2.toFixed(2)}"/>`;
|
||||
const x1 = centerX + Math.cos(angle) * innerRadius;
|
||||
const y1 = centerY + Math.sin(angle) * innerRadius;
|
||||
const x2 = centerX + Math.cos(angle) * outerRadius;
|
||||
const y2 = centerY + Math.sin(angle) * outerRadius;
|
||||
return `<path opacity="${opacity.toFixed(2)}" stroke-width="${strokeWidth}" d="M${x1.toFixed(2)} ${y1.toFixed(2)} ${x2.toFixed(2)} ${y2.toFixed(2)}"/>`;
|
||||
}).join('');
|
||||
const spinnerSpokes = makeSpinner(24, 24, 3, 5);
|
||||
const largeSpinnerSpokes = makeSpinner(16, 16, 5.2, 10.2, 2.2);
|
||||
const arrow = '<path fill="#f6efe2" d="M4 3l9 21 2.4-8.5L24 13z"/><path d="M15.4 15.5 21 21"/>';
|
||||
const pointer = '<path fill="#f6efe2" d="M9 14V5a2.2 2.2 0 0 1 4.4 0v7.5"/><path d="M13.4 12V8.5a2.1 2.1 0 0 1 4.2 0v5"/><path d="M17.6 14v-1.7a2.1 2.1 0 0 1 4.2 0v4.2A8.1 8.1 0 0 1 13.7 24h-1.2a6.8 6.8 0 0 1-5.5-3L3.6 15a2.1 2.1 0 0 1 3.4-2.4l2 2.5"/>';
|
||||
const pointer = '<path fill="#f6efe2" d="M9 14.8V5a2.2 2.2 0 0 1 4.4 0v6.8V8.5a2.1 2.1 0 0 1 4.2 0V13v-.7a2.1 2.1 0 0 1 4.2 0v4.2A8.1 8.1 0 0 1 13.7 24h-1.2a6.8 6.8 0 0 1-5.5-3L3.6 15a2.1 2.1 0 0 1 3.4-2.4L9 14.8z"/><path d="M9 14.8V5a2.2 2.2 0 0 1 4.4 0v7.5"/><path d="M13.4 12V8.5a2.1 2.1 0 0 1 4.2 0v5"/><path d="M17.6 14v-1.7a2.1 2.1 0 0 1 4.2 0v4.2A8.1 8.1 0 0 1 13.7 24h-1.2a6.8 6.8 0 0 1-5.5-3L3.6 15a2.1 2.1 0 0 1 3.4-2.4l2 2.5"/>';
|
||||
const feather = '<path fill="#f6efe2" d="M5 26c5.8-1.7 12.5-7.9 18.4-20.7 2.3 7.6-.2 16.1-11.8 19.9"/><path d="M5 26c4.8-4.5 8.7-9.2 13-15"/><path d="M12 25.2 5 26"/>';
|
||||
const speaker = '<g transform="translate(20 2) scale(.48)"><path fill="#f6efe2" d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.5 8.5a5 5 0 0 1 0 7"/><path d="M19 5a10 10 0 0 1 0 14"/></g>';
|
||||
const spinner = `<g>${spinnerSpokes}</g>`;
|
||||
const largeSpinner = `<g>${largeSpinnerSpokes}</g>`;
|
||||
const hourglassSand = frame % 4 < 2 ? '<path d="M5.7 4.7h4.6"/><path d="M6.7 7h2.6"/>' : '<path d="M6.7 12h2.6"/><path d="M5.7 14.3h4.6"/>';
|
||||
const hourglass = `<g transform="translate(1 17) scale(.82)"><path fill="#f6efe2" d="M4 2h8M4 16h8M10.8 2v3.1a2 2 0 0 1-.6 1.4L8 8.5l-2.2-2A2 2 0 0 1 5.2 5.1V2M5.2 16v-3.1a2 2 0 0 1 .6-1.4L8 9.5l2.2 2a2 2 0 0 1 .6 1.4V16"/>${hourglassSand}</g>`;
|
||||
const icons = {
|
||||
'default': `<svg ${common}>${arrow}</svg>`,
|
||||
'pointer': `<svg ${common}>${pointer}</svg>`,
|
||||
'command-waiting': `<svg ${common}>${arrow}${hourglass}</svg>`,
|
||||
'waiting-generating': `<svg ${common}>${arrow}${spinner}</svg>`,
|
||||
'command-waiting': `<svg ${common}>${largeSpinner}${hourglass}</svg>`,
|
||||
'waiting-generating': `<svg ${common}>${largeSpinner}</svg>`,
|
||||
'playing-generating': `<svg ${common}>${feather}${speaker}${spinner}</svg>`,
|
||||
'playing-ready': `<svg ${common}>${feather}${speaker}</svg>`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user