diff --git a/client/public/version.js b/client/public/version.js index 568b7ea..420dbc7 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -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.21 R129"; +window.CHGRID_WEB_VERSION = "2026.02.22 R130"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/audio/itemEmitRuntime.ts b/client/src/audio/itemEmitRuntime.ts index dfc02f7..ddf4e43 100644 --- a/client/src/audio/itemEmitRuntime.ts +++ b/client/src/audio/itemEmitRuntime.ts @@ -110,7 +110,7 @@ export class ItemEmitRuntime { const effect = normalizeRadioEffect(item.params.emitEffect); const effectValue = normalizeRadioEffectValue(item.params.emitEffectValue); const effectRuntime = connectEffectChain(audioCtx, effectInput, gain, effect, effectValue); - element.playbackRate = resolveEmitPlaybackRate(item.params.emitSpeed); + element.playbackRate = resolveEmitPlaybackRate(item.params.emitSoundSpeed); if (this.audio.supportsStereoPanner()) { panner = audioCtx.createStereoPanner(); gain.connect(panner).connect(audioCtx.destination); @@ -148,7 +148,7 @@ export class ItemEmitRuntime { output.effect = effect; output.effectValue = effectValue; } - const nextPlaybackRate = resolveEmitPlaybackRate(item.params.emitSpeed); + const nextPlaybackRate = resolveEmitPlaybackRate(item.params.emitSoundSpeed); if (Math.abs(output.element.playbackRate - nextPlaybackRate) > 0.001) { output.element.playbackRate = nextPlaybackRate; } diff --git a/client/src/items/itemRegistry.ts b/client/src/items/itemRegistry.ts index f3880f9..c13489a 100644 --- a/client/src/items/itemRegistry.ts +++ b/client/src/items/itemRegistry.ts @@ -52,7 +52,7 @@ const DEFAULT_ITEM_TYPE_EDITABLE_PROPERTIES: Record = { dice: ['title', 'sides', 'number'], wheel: ['title', 'spaces'], clock: ['title', 'timeZone', 'use24Hour'], - widget: ['title', 'enabled', 'directional', 'facing', 'emitRange', 'emitVolume', 'emitSpeed', 'emitEffect', 'emitEffectValue', 'useSound', 'emitSound'], + widget: ['title', 'enabled', 'directional', 'facing', 'emitRange', 'emitVolume', 'emitSoundSpeed', 'emitEffect', 'emitEffectValue', 'useSound', 'emitSound'], }; const DEFAULT_ITEM_TYPE_GLOBAL_PROPERTIES: Record> = { @@ -196,7 +196,7 @@ export function itemPropertyLabel(key: string): string { if (key === 'emitRange') return 'emit range'; if (key === 'mediaVolume') return 'media volume'; if (key === 'emitVolume') return 'emit volume'; - if (key === 'emitSpeed') return 'emit speed'; + if (key === 'emitSoundSpeed') return 'emit sound speed'; if (key === 'mediaChannel') return 'media channel'; if (key === 'mediaEffect') return 'media effect'; if (key === 'mediaEffectValue') return 'media effect value'; diff --git a/client/src/main.ts b/client/src/main.ts index 4c2fa53..996329a 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -753,7 +753,7 @@ function inferItemPropertyValueType(item: WorldItem, key: string): string | unde key === 'version' || key === 'mediaVolume' || key === 'emitVolume' || - key === 'emitSpeed' || + key === 'emitSoundSpeed' || key === 'mediaEffectValue' || key === 'emitEffectValue' || key === 'facing' || @@ -2159,14 +2159,14 @@ function handleItemPropertyEditModeInput(code: string, key: string, ctrlKey: boo return; } signaling.send({ type: 'item_update', itemId, params: { emitVolume: parsed.value } }); - } else if (propertyKey === 'emitSpeed') { + } else if (propertyKey === 'emitSoundSpeed') { const parsed = validateNumericItemPropertyInput(item, propertyKey, value, true); if (!parsed.ok) { updateStatus(parsed.message); audio.sfxUiCancel(); return; } - signaling.send({ type: 'item_update', itemId, params: { emitSpeed: parsed.value } }); + signaling.send({ type: 'item_update', itemId, params: { emitSoundSpeed: parsed.value } }); } else if (propertyKey === 'mediaEffect' || propertyKey === 'emitEffect') { const normalized = value.trim().toLowerCase() as EffectId; if (!EFFECT_IDS.has(normalized)) { diff --git a/docs/item-schema.md b/docs/item-schema.md index 4d68bb4..0d01375 100644 --- a/docs/item-schema.md +++ b/docs/item-schema.md @@ -137,7 +137,7 @@ "facing": 0, "emitRange": 15, "emitVolume": 100, - "emitSpeed": 50, + "emitSoundSpeed": 50, "emitEffect": "off", "emitEffectValue": 50, "useSound": "", @@ -150,7 +150,7 @@ - `facing`: number, range `0-360`, precision `0.1`. - `emitRange`: integer, range `1-20`, default `15`. - `emitVolume`: integer, range `0-100`, default `100`. -- `emitSpeed`: integer, range `0-100`, default `50`; maps to playback rate (`0=0.5x`, `50=1.0x`, `100=2.0x`). +- `emitSoundSpeed`: integer, range `0-100`, default `50`; maps to playback rate (`0=0.5x`, `50=1.0x`, `100=2.0x`). - `emitEffect`: one of `reverb | echo | flanger | high_pass | low_pass | off`, default `off`. - `emitEffectValue`: number, range `0-100`, precision `0.1`, default `50`. - `useSound`: empty, filename (assumed under `sounds/`), or full URL. diff --git a/docs/item-types.md b/docs/item-types.md index e74b7c2..8199da2 100644 --- a/docs/item-types.md +++ b/docs/item-types.md @@ -120,7 +120,7 @@ This is behavior-focused documentation for item types and their defaults. - `facing=0` - `emitRange=15` - `emitVolume=100` - - `emitSpeed=50` + - `emitSoundSpeed=50` - `emitEffect="off"` - `emitEffectValue=50` - `useSound=""` @@ -141,7 +141,7 @@ This is behavior-focused documentation for item types and their defaults. - `facing`: number `0..360` with `0.1` precision - `emitRange`: integer `1..20` - `emitVolume`: integer `0..100` -- `emitSpeed`: integer `0..100` (`0=0.5x`, `50=1.0x`, `100=2.0x`) +- `emitSoundSpeed`: integer `0..100` (`0=0.5x`, `50=1.0x`, `100=2.0x`) - `emitEffect`: `reverb | echo | flanger | high_pass | low_pass | off` - `emitEffectValue`: number `0..100` with `0.1` precision - `useSound`: empty, filename (assumed under `sounds/`), or full URL diff --git a/server/app/items/widget.py b/server/app/items/widget.py index 0734869..cc9a5ac 100644 --- a/server/app/items/widget.py +++ b/server/app/items/widget.py @@ -17,7 +17,7 @@ EDITABLE_PROPERTIES: tuple[str, ...] = ( "facing", "emitRange", "emitVolume", - "emitSpeed", + "emitSoundSpeed", "emitEffect", "emitEffectValue", "useSound", @@ -36,7 +36,7 @@ DEFAULT_PARAMS: dict = { "facing": 0, "emitRange": 15, "emitVolume": 100, - "emitSpeed": 50, + "emitSoundSpeed": 50, "emitEffect": "off", "emitEffectValue": 50, "useSound": "", @@ -63,7 +63,7 @@ PROPERTY_METADATA: dict[str, dict[str, object]] = { "tooltip": "Emitted sound volume percent.", "range": {"min": 0, "max": 100, "step": 1}, }, - "emitSpeed": { + "emitSoundSpeed": { "valueType": "number", "tooltip": "Playback speed/pitch percent for emitted sound. 50 is normal, 0 is half, 100 is double.", "range": {"min": 0, "max": 100, "step": 1}, @@ -132,12 +132,12 @@ def validate_update(item: WorldItem, next_params: dict) -> dict: next_params["emitVolume"] = emit_volume try: - emit_speed = int(next_params.get("emitSpeed", item.params.get("emitSpeed", 50))) + emit_speed = int(next_params.get("emitSoundSpeed", item.params.get("emitSoundSpeed", 50))) except (TypeError, ValueError) as exc: - raise ValueError("emitSpeed must be an integer between 0 and 100.") from exc + raise ValueError("emitSoundSpeed must be an integer between 0 and 100.") from exc if not (0 <= emit_speed <= 100): - raise ValueError("emitSpeed must be between 0 and 100.") - next_params["emitSpeed"] = emit_speed + raise ValueError("emitSoundSpeed must be between 0 and 100.") + next_params["emitSoundSpeed"] = emit_speed emit_effect = str(next_params.get("emitEffect", item.params.get("emitEffect", "off"))).strip().lower() if emit_effect not in EFFECT_OPTIONS: diff --git a/server/tests/test_item_use_cooldown.py b/server/tests/test_item_use_cooldown.py index 65e2e7e..98d06b6 100644 --- a/server/tests/test_item_use_cooldown.py +++ b/server/tests/test_item_use_cooldown.py @@ -292,7 +292,7 @@ async def test_widget_update_and_use(monkeypatch: pytest.MonkeyPatch) -> None: "facing": 123.4, "emitRange": 7, "emitVolume": 42, - "emitSpeed": 25, + "emitSoundSpeed": 25, "emitEffect": "reverb", "emitEffectValue": 63.2, "useSound": "ping.ogg", @@ -306,7 +306,7 @@ async def test_widget_update_and_use(monkeypatch: pytest.MonkeyPatch) -> None: assert item.params.get("facing") == 123.4 assert item.params.get("emitRange") == 7 assert item.params.get("emitVolume") == 42 - assert item.params.get("emitSpeed") == 25 + assert item.params.get("emitSoundSpeed") == 25 assert item.params.get("emitEffect") == "reverb" assert item.params.get("emitEffectValue") == 63.2 assert item.params.get("useSound") == "sounds/ping.ogg" @@ -326,7 +326,7 @@ async def test_widget_update_and_use(monkeypatch: pytest.MonkeyPatch) -> None: await server._handle_message( client, - json.dumps({"type": "item_update", "itemId": item.id, "params": {"emitSpeed": 101}}), + json.dumps({"type": "item_update", "itemId": item.id, "params": {"emitSoundSpeed": 101}}), ) assert send_payloads[-1].ok is False - assert "emitspeed must be between 0 and 100" in send_payloads[-1].message.lower() + assert "emitsoundspeed must be between 0 and 100" in send_payloads[-1].message.lower()