Checkpoint current UI and ink integration state
This commit is contained in:
@@ -14,11 +14,13 @@ class UIInputHandlerModule extends BaseModule {
|
||||
this.commandHistoryElement = null;
|
||||
|
||||
// Input state
|
||||
this.inputEnabled = true;
|
||||
this.inputEnabled = false;
|
||||
this.historyIndex = -1;
|
||||
this.commandHistory = [];
|
||||
this.inputBuffer = '';
|
||||
this.inputMode = 'text';
|
||||
this.inputMode = 'none';
|
||||
this.cursorAnimationTimer = null;
|
||||
this.cursorAnimationFrame = 0;
|
||||
|
||||
// Bind methods using the parent class bindMethods utility
|
||||
this.bindMethods([
|
||||
@@ -37,6 +39,10 @@ class UIInputHandlerModule extends BaseModule {
|
||||
'setProcessState',
|
||||
'setInputAvailability',
|
||||
'setMode',
|
||||
'setInputModeDataset',
|
||||
'installMouseCursors',
|
||||
'startMouseCursorAnimation',
|
||||
'stopMouseCursorAnimation',
|
||||
'clearHistory'
|
||||
]);
|
||||
|
||||
@@ -62,11 +68,12 @@ class UIInputHandlerModule extends BaseModule {
|
||||
this.reportProgress(60, 'Setting up input elements');
|
||||
|
||||
this.setupInputElements();
|
||||
this.installMouseCursors();
|
||||
this.addEventListener(document, 'story:process-state', (event) => {
|
||||
this.setProcessState(event.detail?.state || 'ready', event.detail || {});
|
||||
});
|
||||
this.addEventListener(document, 'story:input-mode', (event) => {
|
||||
this.setMode(event.detail || 'text');
|
||||
this.setMode(event.detail || 'none');
|
||||
});
|
||||
this.addEventListener(document, 'story:turn-start', (event) => {
|
||||
this.bindHistoryToTurn(event.detail?.turnId);
|
||||
@@ -157,6 +164,12 @@ class UIInputHandlerModule extends BaseModule {
|
||||
|
||||
pageLeft.appendChild(choicesContainer);
|
||||
}
|
||||
|
||||
if (!document.getElementById('left_control_separator')) {
|
||||
const controlSeparator = document.createElement('div');
|
||||
controlSeparator.id = 'left_control_separator';
|
||||
choicesContainer.insertBefore(controlSeparator, choicesContainer.firstChild);
|
||||
}
|
||||
|
||||
// Create command history container if needed
|
||||
let commandHistory = document.getElementById('command_history');
|
||||
@@ -227,10 +240,8 @@ class UIInputHandlerModule extends BaseModule {
|
||||
// Position the cursor
|
||||
if (playerInput && cursor) {
|
||||
this.positionCursor(playerInput, cursor);
|
||||
this.setProcessState('ready', { reason: 'input-initialized' });
|
||||
this.focusInput();
|
||||
requestAnimationFrame(() => this.focusInput());
|
||||
setTimeout(() => this.focusInput(), 250);
|
||||
this.setInputModeDataset();
|
||||
this.setInputAvailability(false);
|
||||
}
|
||||
|
||||
console.log('UIInputHandler: Input elements setup complete');
|
||||
@@ -312,8 +323,14 @@ class UIInputHandlerModule extends BaseModule {
|
||||
}
|
||||
|
||||
setMode(mode) {
|
||||
this.inputMode = ['text', 'choice', 'end'].includes(mode) ? mode : 'text';
|
||||
this.setInputAvailability(this.inputMode === 'text');
|
||||
this.inputMode = ['text', 'choice', 'end', 'none'].includes(mode) ? mode : 'none';
|
||||
this.setInputModeDataset();
|
||||
const state = document.documentElement.dataset.processState || 'loading';
|
||||
this.setInputAvailability(this.inputMode === 'text' && state === 'ready');
|
||||
}
|
||||
|
||||
setInputModeDataset() {
|
||||
document.documentElement.dataset.inputMode = this.inputMode || 'none';
|
||||
}
|
||||
|
||||
applyMouseCursor(state) {
|
||||
@@ -326,28 +343,79 @@ class UIInputHandlerModule extends BaseModule {
|
||||
const cursor = this.getMouseCursor(state);
|
||||
if (cursor) {
|
||||
root.style.setProperty('--process-cursor', cursor);
|
||||
this.startMouseCursorAnimation(state);
|
||||
} else {
|
||||
root.style.removeProperty('--process-cursor');
|
||||
this.stopMouseCursorAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
installMouseCursors() {
|
||||
const root = document.documentElement;
|
||||
if (!root) return;
|
||||
root.style.setProperty('--default-cursor', this.buildMouseCursor('default', 'default', 4, 3));
|
||||
root.style.setProperty('--pointer-cursor', this.buildMouseCursor('pointer', 'pointer', 7, 2));
|
||||
}
|
||||
|
||||
getMouseCursor(state) {
|
||||
if (state === 'ready') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const svg = this.getMouseCursorSvg(state);
|
||||
const fallback = state === 'command-waiting' ? 'wait' : 'progress';
|
||||
return `url("${this.toCursorDataUrl(svg)}") 12 12, ${fallback}`;
|
||||
return this.buildMouseCursor(state, fallback, 12, 12, this.cursorAnimationFrame);
|
||||
}
|
||||
|
||||
getMouseCursorSvg(state) {
|
||||
buildMouseCursor(state, fallback = 'default', hotspotX = 12, hotspotY = 12, frame = 0) {
|
||||
const svg = this.getMouseCursorSvg(state, frame);
|
||||
return `url("${this.toCursorDataUrl(svg)}") ${hotspotX} ${hotspotY}, ${fallback}`;
|
||||
}
|
||||
|
||||
startMouseCursorAnimation(state) {
|
||||
const animatedStates = new Set(['command-waiting', 'waiting-generating', 'playing-generating']);
|
||||
if (!animatedStates.has(state)) {
|
||||
this.stopMouseCursorAnimation();
|
||||
return;
|
||||
}
|
||||
if (this.cursorAnimationTimer) return;
|
||||
this.cursorAnimationTimer = window.setInterval(() => {
|
||||
const currentState = document.documentElement.dataset.processState || 'ready';
|
||||
if (!animatedStates.has(currentState)) {
|
||||
this.stopMouseCursorAnimation();
|
||||
return;
|
||||
}
|
||||
this.cursorAnimationFrame = (this.cursorAnimationFrame + 1) % 8;
|
||||
document.documentElement.style.setProperty('--process-cursor', this.getMouseCursor(currentState));
|
||||
}, 160);
|
||||
}
|
||||
|
||||
stopMouseCursorAnimation() {
|
||||
if (this.cursorAnimationTimer) {
|
||||
window.clearInterval(this.cursorAnimationTimer);
|
||||
this.cursorAnimationTimer = null;
|
||||
}
|
||||
this.cursorAnimationFrame = 0;
|
||||
}
|
||||
|
||||
getMouseCursorSvg(state, frame = 0) {
|
||||
const stroke = '#222222';
|
||||
const common = `xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`;
|
||||
const spinnerSpokes = Array.from({ length: 8 }, (_, index) => {
|
||||
const opacity = 0.25 + (((index + frame) % 8) / 7) * 0.75;
|
||||
const angle = (index * 45) * Math.PI / 180;
|
||||
const x1 = 12 + Math.cos(angle) * 6;
|
||||
const y1 = 12 + Math.sin(angle) * 6;
|
||||
const x2 = 12 + Math.cos(angle) * 9;
|
||||
const y2 = 12 + Math.sin(angle) * 9;
|
||||
return `<path opacity="${opacity.toFixed(2)}" d="M${x1.toFixed(2)} ${y1.toFixed(2)} ${x2.toFixed(2)} ${y2.toFixed(2)}"/>`;
|
||||
}).join('');
|
||||
const sandTop = frame % 4 < 2 ? '<path d="M9 5h6"/><path d="M10 8h4"/>' : '<path d="M10 16h4"/><path d="M9 19h6"/>';
|
||||
const icons = {
|
||||
'command-waiting': `<svg ${common}><path d="M5 22h14"/><path d="M5 2h14"/><path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"/><path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/></svg>`,
|
||||
'waiting-generating': `<svg ${common}><path d="M12 2v4"/><path d="M12 18v4"/><path d="m4.93 4.93 2.83 2.83"/><path d="m16.24 16.24 2.83 2.83"/><path d="M2 12h4"/><path d="M18 12h4"/><path d="m4.93 19.07 2.83-2.83"/><path d="m16.24 7.76 2.83-2.83"/></svg>`,
|
||||
'playing-generating': `<svg ${common}><path d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M12 2v3"/><path d="M12 19v3"/></svg>`,
|
||||
'default': `<svg ${common}><path d="M4 3l7.5 18 2.1-7.4L21 11z"/><path d="M13.6 13.6 18 18"/></svg>`,
|
||||
'pointer': `<svg ${common}><path d="M8 11V4a2 2 0 1 1 4 0v6"/><path d="M12 10V7a2 2 0 1 1 4 0v5"/><path d="M16 12v-1a2 2 0 1 1 4 0v3a7 7 0 0 1-7 7h-1a6 6 0 0 1-5-2.7L3 13a2 2 0 0 1 3-2.6l2 2.1"/></svg>`,
|
||||
'command-waiting': `<svg ${common}><path d="M5 22h14"/><path d="M5 2h14"/><path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"/><path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/>${sandTop}</svg>`,
|
||||
'waiting-generating': `<svg ${common}>${spinnerSpokes}</svg>`,
|
||||
'playing-generating': `<svg ${common}><path d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>${spinnerSpokes}</svg>`,
|
||||
'playing-ready': `<svg ${common}><path d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></svg>`
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user