Use native Ctrl+V paste and add media stream status diagnostics
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Maintainer-controlled web client version.
|
||||
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
||||
window.CHGRID_WEB_VERSION = "2026.02.22 R137";
|
||||
window.CHGRID_WEB_VERSION = "2026.02.22 R138";
|
||||
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
||||
window.CHGRID_TIME_ZONE = "America/Detroit";
|
||||
|
||||
@@ -131,13 +131,23 @@ type RadioSpatialConfig = {
|
||||
export class RadioStationRuntime {
|
||||
private readonly sharedRadioSources = new Map<string, SharedRadioSource>();
|
||||
private readonly itemRadioOutputs = new Map<string, ItemRadioOutput>();
|
||||
private readonly lastStreamStatusAt = new Map<string, number>();
|
||||
private layerEnabled = true;
|
||||
|
||||
constructor(
|
||||
private readonly audio: AudioEngine,
|
||||
private readonly getSpatialConfig: (item: WorldItem) => RadioSpatialConfig,
|
||||
private readonly onStreamStatus?: (message: string) => void,
|
||||
) {}
|
||||
|
||||
private reportStreamStatus(message: string, dedupeKey: string): void {
|
||||
const now = Date.now();
|
||||
const lastAt = this.lastStreamStatusAt.get(dedupeKey) ?? 0;
|
||||
if (now - lastAt < 3000) return;
|
||||
this.lastStreamStatusAt.set(dedupeKey, now);
|
||||
this.onStreamStatus?.(message);
|
||||
}
|
||||
|
||||
cleanup(itemId: string): void {
|
||||
const output = this.itemRadioOutputs.get(itemId);
|
||||
if (!output) return;
|
||||
@@ -286,6 +296,18 @@ export class RadioStationRuntime {
|
||||
element.crossOrigin = 'anonymous';
|
||||
element.loop = true;
|
||||
element.preload = 'none';
|
||||
element.addEventListener('error', () => {
|
||||
this.reportStreamStatus(
|
||||
`Media stream failed: ${streamUrl}`,
|
||||
`error:${streamUrl}`,
|
||||
);
|
||||
});
|
||||
element.addEventListener('canplay', () => {
|
||||
this.reportStreamStatus(
|
||||
`Media stream ready: ${streamUrl}`,
|
||||
`ready:${streamUrl}`,
|
||||
);
|
||||
});
|
||||
const source = audioCtx.createMediaElementSource(element);
|
||||
void element.play().catch(() => undefined);
|
||||
const shared: SharedRadioSource = {
|
||||
|
||||
@@ -178,7 +178,7 @@ let outputMode = localStorage.getItem(AUDIO_OUTPUT_MODE_STORAGE_KEY) === 'mono'
|
||||
let connecting = false;
|
||||
const messageBuffer: string[] = [];
|
||||
let messageCursor = -1;
|
||||
const radioRuntime = new RadioStationRuntime(audio, getItemSpatialConfig);
|
||||
const radioRuntime = new RadioStationRuntime(audio, getItemSpatialConfig, (message) => updateStatus(message));
|
||||
const itemEmitRuntime = new ItemEmitRuntime(audio, resolveIncomingSoundUrl, getItemSpatialConfig);
|
||||
let internalClipboardText = '';
|
||||
let replaceTextOnNextType = false;
|
||||
@@ -629,21 +629,6 @@ function pasteIntoActiveTextInput(raw: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handlePasteShortcut(): Promise<void> {
|
||||
let pasted = internalClipboardText;
|
||||
try {
|
||||
const clipboardText = await navigator.clipboard?.readText();
|
||||
if (typeof clipboardText === 'string') {
|
||||
pasted = clipboardText;
|
||||
internalClipboardText = clipboardText;
|
||||
}
|
||||
} catch {
|
||||
// Clipboard read can fail without user gesture/permissions; fallback to internal clipboard.
|
||||
}
|
||||
if (!pasteIntoActiveTextInput(pasted)) return;
|
||||
updateStatus('pasted');
|
||||
}
|
||||
|
||||
function isTextEditingMode(mode: typeof state.mode): boolean {
|
||||
return mode === 'nickname' || mode === 'chat' || mode === 'itemPropertyEdit';
|
||||
}
|
||||
@@ -2398,7 +2383,8 @@ function setupInputHandlers(): void {
|
||||
if (event.altKey) return;
|
||||
if (event.ctrlKey && !isTextEditingMode(state.mode)) return;
|
||||
|
||||
if (state.mode !== 'normal' || !code.startsWith('Arrow')) {
|
||||
const isNativePasteShortcut = event.ctrlKey && isTextEditingMode(state.mode) && code === 'KeyV';
|
||||
if ((state.mode !== 'normal' || !code.startsWith('Arrow')) && !isNativePasteShortcut) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@@ -2420,10 +2406,6 @@ function setupInputHandlers(): void {
|
||||
updateStatus('cut');
|
||||
return;
|
||||
}
|
||||
if (code === 'KeyV') {
|
||||
void handlePasteShortcut();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTypingKey(code) && state.keysPressed[code]) return;
|
||||
|
||||
Reference in New Issue
Block a user