diff --git a/.gitignore b/.gitignore index 33bd037..3736e04 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ client_web/dist var deploy +clients/**/dist diff --git a/clients/web-game/assets/style.css b/clients/web-game/assets/style.css index 3034f38..341be19 100644 --- a/clients/web-game/assets/style.css +++ b/clients/web-game/assets/style.css @@ -1194,3 +1194,20 @@ body { color: var(--ui-red-accent); font-style: italic; } + + +.auth-badge { + font-size: 0.8rem; + text-align: center; + padding: 0.35rem 0.6rem; + border-radius: 5px; +} +.auth-badge--in { background: rgba(96,165,250,0.15); color: #93c5fd; } +.auth-badge--out { background: rgba(148,163,184,0.1); color: #64748b; } +.auth-badge a { color: #60a5fa; } + +.playing-as { + font-size: 0.8rem; + color: #64748b; + text-align: center; +} diff --git a/clients/web-game/dist/client_web-4248a2b78bb5a03.js b/clients/web-game/dist/client_web-4248a2b78bb5a03.js deleted file mode 100644 index 16381a5..0000000 --- a/clients/web-game/dist/client_web-4248a2b78bb5a03.js +++ /dev/null @@ -1,1173 +0,0 @@ -export class IntoUnderlyingByteSource { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - IntoUnderlyingByteSourceFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_intounderlyingbytesource_free(ptr, 0); - } - /** - * @returns {number} - */ - get autoAllocateChunkSize() { - const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); - return ret >>> 0; - } - cancel() { - const ptr = this.__destroy_into_raw(); - wasm.intounderlyingbytesource_cancel(ptr); - } - /** - * @param {ReadableByteStreamController} controller - * @returns {Promise} - */ - pull(controller) { - const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, controller); - return ret; - } - /** - * @param {ReadableByteStreamController} controller - */ - start(controller) { - wasm.intounderlyingbytesource_start(this.__wbg_ptr, controller); - } - /** - * @returns {ReadableStreamType} - */ - get type() { - const ret = wasm.intounderlyingbytesource_type(this.__wbg_ptr); - return __wbindgen_enum_ReadableStreamType[ret]; - } -} -if (Symbol.dispose) IntoUnderlyingByteSource.prototype[Symbol.dispose] = IntoUnderlyingByteSource.prototype.free; - -export class IntoUnderlyingSink { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - IntoUnderlyingSinkFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_intounderlyingsink_free(ptr, 0); - } - /** - * @param {any} reason - * @returns {Promise} - */ - abort(reason) { - const ptr = this.__destroy_into_raw(); - const ret = wasm.intounderlyingsink_abort(ptr, reason); - return ret; - } - /** - * @returns {Promise} - */ - close() { - const ptr = this.__destroy_into_raw(); - const ret = wasm.intounderlyingsink_close(ptr); - return ret; - } - /** - * @param {any} chunk - * @returns {Promise} - */ - write(chunk) { - const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, chunk); - return ret; - } -} -if (Symbol.dispose) IntoUnderlyingSink.prototype[Symbol.dispose] = IntoUnderlyingSink.prototype.free; - -export class IntoUnderlyingSource { - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - IntoUnderlyingSourceFinalization.unregister(this); - return ptr; - } - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_intounderlyingsource_free(ptr, 0); - } - cancel() { - const ptr = this.__destroy_into_raw(); - wasm.intounderlyingsource_cancel(ptr); - } - /** - * @param {ReadableStreamDefaultController} controller - * @returns {Promise} - */ - pull(controller) { - const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, controller); - return ret; - } -} -if (Symbol.dispose) IntoUnderlyingSource.prototype[Symbol.dispose] = IntoUnderlyingSource.prototype.free; -function __wbg_get_imports() { - const import0 = { - __proto__: null, - __wbg___wbindgen_debug_string_ab4b34d23d6778bd: function(arg0, arg1) { - const ret = debugString(arg1); - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, - __wbg___wbindgen_in_a5d8b22e52b24dd1: function(arg0, arg1) { - const ret = arg0 in arg1; - return ret; - }, - __wbg___wbindgen_is_falsy_c07bb72123e65555: function(arg0) { - const ret = !arg0; - return ret; - }, - __wbg___wbindgen_is_function_3baa9db1a987f47d: function(arg0) { - const ret = typeof(arg0) === 'function'; - return ret; - }, - __wbg___wbindgen_is_null_52ff4ec04186736f: function(arg0) { - const ret = arg0 === null; - return ret; - }, - __wbg___wbindgen_is_string_6df3bf7ef1164ed3: function(arg0) { - const ret = typeof(arg0) === 'string'; - return ret; - }, - __wbg___wbindgen_is_undefined_29a43b4d42920abd: function(arg0) { - const ret = arg0 === undefined; - return ret; - }, - __wbg___wbindgen_string_get_7ed5322991caaec5: function(arg0, arg1) { - const obj = arg1; - const ret = typeof(obj) === 'string' ? obj : undefined; - var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, - __wbg___wbindgen_throw_6b64449b9b9ed33c: function(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); - }, - __wbg__wbg_cb_unref_b46c9b5a9f08ec37: function(arg0) { - arg0._wbg_cb_unref(); - }, - __wbg_addEventListener_79f868f51ae88579: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3, arg4); - }, arguments); }, - __wbg_addEventListener_8176dab41b09531c: function() { return handleError(function (arg0, arg1, arg2, arg3) { - arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3); - }, arguments); }, - __wbg_add_0cfb2ab24caa9888: function() { return handleError(function (arg0, arg1, arg2) { - arg0.add(getStringFromWasm0(arg1, arg2)); - }, arguments); }, - __wbg_body_c7b35a55457167ba: function(arg0) { - const ret = arg0.body; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_buffer_d0f5ea0926a691fd: function(arg0) { - const ret = arg0.buffer; - return ret; - }, - __wbg_byobRequest_dc6aed9db01b12c6: function(arg0) { - const ret = arg0.byobRequest; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_byteLength_3e660e5661f3327e: function(arg0) { - const ret = arg0.byteLength; - return ret; - }, - __wbg_byteOffset_ecd62abe44dd28d4: function(arg0) { - const ret = arg0.byteOffset; - return ret; - }, - __wbg_call_a24592a6f349a97e: function() { return handleError(function (arg0, arg1, arg2) { - const ret = arg0.call(arg1, arg2); - return ret; - }, arguments); }, - __wbg_cancelBubble_56aa5b315d711482: function(arg0) { - const ret = arg0.cancelBubble; - return ret; - }, - __wbg_classList_a4e8d7553b666e6d: function(arg0) { - const ret = arg0.classList; - return ret; - }, - __wbg_clearTimeout_113b1cde814ec762: function(arg0) { - const ret = clearTimeout(arg0); - return ret; - }, - __wbg_clearTimeout_1a62f3563b1611b3: function(arg0, arg1) { - arg0.clearTimeout(arg1); - }, - __wbg_cloneNode_50658ff5fec44693: function() { return handleError(function (arg0, arg1) { - const ret = arg0.cloneNode(arg1 !== 0); - return ret; - }, arguments); }, - __wbg_cloneNode_eb01fe238729dac4: function() { return handleError(function (arg0) { - const ret = arg0.cloneNode(); - return ret; - }, arguments); }, - __wbg_close_0aa6756f298a2c2d: function(arg0) { - arg0.close(); - }, - __wbg_close_88106990eea7f544: function() { return handleError(function (arg0) { - arg0.close(); - }, arguments); }, - __wbg_close_e6c8977a002e9e13: function() { return handleError(function (arg0) { - arg0.close(); - }, arguments); }, - __wbg_close_fb954dfaf67b5732: function() { return handleError(function (arg0) { - arg0.close(); - }, arguments); }, - __wbg_composedPath_e2b9e0f5161335eb: function(arg0) { - const ret = arg0.composedPath(); - return ret; - }, - __wbg_connect_301bfaee317657e7: function() { return handleError(function (arg0, arg1) { - const ret = arg0.connect(arg1); - return ret; - }, arguments); }, - __wbg_content_13d0cb7e0ea91c39: function(arg0) { - const ret = arg0.content; - return ret; - }, - __wbg_cookie_d587a65145c1f3ba: function() { return handleError(function (arg0, arg1) { - const ret = arg1.cookie; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, arguments); }, - __wbg_createComment_592a0c17b1cf8cad: function(arg0, arg1, arg2) { - const ret = arg0.createComment(getStringFromWasm0(arg1, arg2)); - return ret; - }, - __wbg_createElementNS_e0e4bbb6e664f948: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - const ret = arg0.createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - return ret; - }, arguments); }, - __wbg_createElement_bbd4c90086fe6f7b: function() { return handleError(function (arg0, arg1, arg2) { - const ret = arg0.createElement(getStringFromWasm0(arg1, arg2)); - return ret; - }, arguments); }, - __wbg_createGain_9f1346a2369109e0: function() { return handleError(function (arg0) { - const ret = arg0.createGain(); - return ret; - }, arguments); }, - __wbg_createOscillator_d891cd791ce0b814: function() { return handleError(function (arg0) { - const ret = arg0.createOscillator(); - return ret; - }, arguments); }, - __wbg_createTextNode_7949043038fd9f7b: function(arg0, arg1, arg2) { - const ret = arg0.createTextNode(getStringFromWasm0(arg1, arg2)); - return ret; - }, - __wbg_currentTime_8e9bfa251075a7d7: function(arg0) { - const ret = arg0.currentTime; - return ret; - }, - __wbg_data_bb9dffdd1e99cf2d: function(arg0) { - const ret = arg0.data; - return ret; - }, - __wbg_deleteProperty_d5f7bd763acbdb44: function() { return handleError(function (arg0, arg1) { - const ret = Reflect.deleteProperty(arg0, arg1); - return ret; - }, arguments); }, - __wbg_destination_7aa167ec1225162d: function(arg0) { - const ret = arg0.destination; - return ret; - }, - __wbg_documentElement_08ce5ecd9e8b21e1: function(arg0) { - const ret = arg0.documentElement; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_document_7a41071f2f439323: function(arg0) { - const ret = arg0.document; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_enqueue_4767ce322820c94d: function() { return handleError(function (arg0, arg1) { - arg0.enqueue(arg1); - }, arguments); }, - __wbg_error_2001591ad2463697: function(arg0) { - console.error(arg0); - }, - __wbg_exponentialRampToValueAtTime_00c2f1771a4804bd: function() { return handleError(function (arg0, arg1, arg2) { - const ret = arg0.exponentialRampToValueAtTime(arg1, arg2); - return ret; - }, arguments); }, - __wbg_firstChild_d4bf03999a23e79a: function(arg0) { - const ret = arg0.firstChild; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_firstElementChild_f67647a589d437a2: function(arg0) { - const ret = arg0.firstElementChild; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_frequency_4f71c695f55b6a54: function(arg0) { - const ret = arg0.frequency; - return ret; - }, - __wbg_gain_af09f4ecb5d66a4e: function(arg0) { - const ret = arg0.gain; - return ret; - }, - __wbg_getItem_7fe1351b9ea3b2f3: function() { return handleError(function (arg0, arg1, arg2, arg3) { - const ret = arg1.getItem(getStringFromWasm0(arg2, arg3)); - var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, arguments); }, - __wbg_getRandomValues_3f44b700395062e5: function() { return handleError(function (arg0, arg1) { - globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1)); - }, arguments); }, - __wbg_get_6011fa3a58f61074: function() { return handleError(function (arg0, arg1) { - const ret = Reflect.get(arg0, arg1); - return ret; - }, arguments); }, - __wbg_get_8360291721e2339f: function(arg0, arg1) { - const ret = arg0[arg1 >>> 0]; - return ret; - }, - __wbg_head_77bab63b2165751c: function(arg0) { - const ret = arg0.head; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_host_207aa9237088c9e9: function(arg0) { - const ret = arg0.host; - return ret; - }, - __wbg_insertBefore_38c7d835a2dcac23: function() { return handleError(function (arg0, arg1, arg2) { - const ret = arg0.insertBefore(arg1, arg2); - return ret; - }, arguments); }, - __wbg_instanceof_ArrayBuffer_7c8433c6ed14ffe3: function(arg0) { - let result; - try { - result = arg0 instanceof ArrayBuffer; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }, - __wbg_instanceof_Blob_10148a11a16aee87: function(arg0) { - let result; - try { - result = arg0 instanceof Blob; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }, - __wbg_instanceof_Element_56c8d987654f359e: function(arg0) { - let result; - try { - result = arg0 instanceof Element; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }, - __wbg_instanceof_Error_6872d63ba7922898: function(arg0) { - let result; - try { - result = arg0 instanceof Error; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }, - __wbg_instanceof_ShadowRoot_d26d95cd2363a2c1: function(arg0) { - let result; - try { - result = arg0 instanceof ShadowRoot; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }, - __wbg_instanceof_Window_cc64c86c8ef9e02b: function(arg0) { - let result; - try { - result = arg0 instanceof Window; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }, - __wbg_languages_53e97082086045ce: function(arg0) { - const ret = arg0.languages; - return ret; - }, - __wbg_length_9f1775224cf1d815: function(arg0) { - const ret = arg0.length; - return ret; - }, - __wbg_localStorage_f5f66b1ffd2486bc: function() { return handleError(function (arg0) { - const ret = arg0.localStorage; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, arguments); }, - __wbg_log_7e1aa9064a1dbdbd: function(arg0) { - console.log(arg0); - }, - __wbg_message_cb4f84ee66e5e341: function(arg0) { - const ret = arg0.message; - return ret; - }, - __wbg_name_d3c35622d14bb080: function(arg0) { - const ret = arg0.name; - return ret; - }, - __wbg_navigator_bc077756492232c5: function(arg0) { - const ret = arg0.navigator; - return ret; - }, - __wbg_new_0a8d011ad814b95a: function() { return handleError(function () { - const ret = new FileReader(); - return ret; - }, arguments); }, - __wbg_new_0c7403db6e782f19: function(arg0) { - const ret = new Uint8Array(arg0); - return ret; - }, - __wbg_new_2a6e9133304ae2bf: function() { return handleError(function (arg0, arg1) { - const ret = new WebSocket(getStringFromWasm0(arg0, arg1)); - return ret; - }, arguments); }, - __wbg_new_5e360d2ff7b9e1c3: function(arg0, arg1) { - const ret = new Error(getStringFromWasm0(arg0, arg1)); - return ret; - }, - __wbg_new_aa8d0fa9762c29bd: function() { - const ret = new Object(); - return ret; - }, - __wbg_new_aadb2b3f13e701cf: function() { return handleError(function (arg0, arg1) { - const ret = new BroadcastChannel(getStringFromWasm0(arg0, arg1)); - return ret; - }, arguments); }, - __wbg_new_df431d05bd05ed26: function() { return handleError(function () { - const ret = new lAudioContext(); - return ret; - }, arguments); }, - __wbg_new_typed_323f37fd55ab048d: function(arg0, arg1) { - try { - var state0 = {a: arg0, b: arg1}; - var cb0 = (arg0, arg1) => { - const a = state0.a; - state0.a = 0; - try { - return wasm_bindgen__convert__closures_____invoke__h42c7f62e44be4353(a, state0.b, arg0, arg1); - } finally { - state0.a = a; - } - }; - const ret = new Promise(cb0); - return ret; - } finally { - state0.a = 0; - } - }, - __wbg_new_with_byte_offset_and_length_01848e8d6a3d49ad: function(arg0, arg1, arg2) { - const ret = new Uint8Array(arg0, arg1 >>> 0, arg2 >>> 0); - return ret; - }, - __wbg_nextSibling_58f635df24be0787: function(arg0) { - const ret = arg0.nextSibling; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_nodeType_1e98f026e15a17e5: function(arg0) { - const ret = arg0.nodeType; - return ret; - }, - __wbg_now_a9b7df1cbee90986: function() { - const ret = Date.now(); - return ret; - }, - __wbg_now_e7c6795a7f81e10f: function(arg0) { - const ret = arg0.now(); - return ret; - }, - __wbg_parentNode_e94744054a57a837: function(arg0) { - const ret = arg0.parentNode; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_performance_3fcf6e32a7e1ed0a: function(arg0) { - const ret = arg0.performance; - return ret; - }, - __wbg_postMessage_f9ee88e3c733baf9: function() { return handleError(function (arg0, arg1) { - arg0.postMessage(arg1); - }, arguments); }, - __wbg_preventDefault_f55c01cb5fd2bcc0: function(arg0) { - arg0.preventDefault(); - }, - __wbg_prototypesetcall_a6b02eb00b0f4ce2: function(arg0, arg1, arg2) { - Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2); - }, - __wbg_queueMicrotask_5d15a957e6aa920e: function(arg0) { - queueMicrotask(arg0); - }, - __wbg_queueMicrotask_f8819e5ffc402f36: function(arg0) { - const ret = arg0.queueMicrotask; - return ret; - }, - __wbg_readAsArrayBuffer_7f1359e61bc15108: function() { return handleError(function (arg0, arg1) { - arg0.readAsArrayBuffer(arg1); - }, arguments); }, - __wbg_removeAttribute_c75ac657c944b3f1: function() { return handleError(function (arg0, arg1, arg2) { - arg0.removeAttribute(getStringFromWasm0(arg1, arg2)); - }, arguments); }, - __wbg_removeEventListener_61405fc9de7dfd6b: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3, arg4); - }, arguments); }, - __wbg_removeEventListener_7bdf07404d9b24bd: function() { return handleError(function (arg0, arg1, arg2, arg3) { - arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3); - }, arguments); }, - __wbg_removeItem_487c385a3066a8ed: function() { return handleError(function (arg0, arg1, arg2) { - arg0.removeItem(getStringFromWasm0(arg1, arg2)); - }, arguments); }, - __wbg_remove_48cb93cf7a6c4260: function(arg0) { - arg0.remove(); - }, - __wbg_remove_8aa602fc502f0448: function() { return handleError(function (arg0, arg1, arg2) { - arg0.remove(getStringFromWasm0(arg1, arg2)); - }, arguments); }, - __wbg_remove_9ffcfa2a5664fa43: function(arg0) { - arg0.remove(); - }, - __wbg_resolve_e6c466bc1052f16c: function(arg0) { - const ret = Promise.resolve(arg0); - return ret; - }, - __wbg_respond_008ca9525ae22847: function() { return handleError(function (arg0, arg1) { - arg0.respond(arg1 >>> 0); - }, arguments); }, - __wbg_result_cadfbcadd3b04647: function() { return handleError(function (arg0) { - const ret = arg0.result; - return ret; - }, arguments); }, - __wbg_run_0b0a622deae25fda: function(arg0, arg1, arg2) { - try { - var state0 = {a: arg1, b: arg2}; - var cb0 = () => { - const a = state0.a; - state0.a = 0; - try { - return wasm_bindgen__convert__closures_____invoke__hf0f977607302985a(a, state0.b, ); - } finally { - state0.a = a; - } - }; - const ret = arg0.run(cb0); - return ret; - } finally { - state0.a = 0; - } - }, - __wbg_send_15358dbe221c6258: function() { return handleError(function (arg0, arg1, arg2) { - arg0.send(getStringFromWasm0(arg1, arg2)); - }, arguments); }, - __wbg_send_186c85704c7f2d00: function() { return handleError(function (arg0, arg1, arg2) { - arg0.send(getArrayU8FromWasm0(arg1, arg2)); - }, arguments); }, - __wbg_setAttribute_6fde4098d274155c: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - arg0.setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - }, arguments); }, - __wbg_setItem_e6399d3faae141dc: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { - arg0.setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); - }, arguments); }, - __wbg_setTimeout_d8786dd31f90da0f: function() { return handleError(function (arg0, arg1, arg2) { - const ret = arg0.setTimeout(arg1, arg2); - return ret; - }, arguments); }, - __wbg_setTimeout_ef24d2fc3ad97385: function() { return handleError(function (arg0, arg1) { - const ret = setTimeout(arg0, arg1); - return ret; - }, arguments); }, - __wbg_setValueAtTime_f2282afd259bb493: function() { return handleError(function (arg0, arg1, arg2) { - const ret = arg0.setValueAtTime(arg1, arg2); - return ret; - }, arguments); }, - __wbg_set_022bee52d0b05b19: function() { return handleError(function (arg0, arg1, arg2) { - const ret = Reflect.set(arg0, arg1, arg2); - return ret; - }, arguments); }, - __wbg_set_3d484eb794afec82: function(arg0, arg1, arg2) { - arg0.set(getArrayU8FromWasm0(arg1, arg2)); - }, - __wbg_set_binaryType_770e68648ca5e83d: function(arg0, arg1) { - arg0.binaryType = __wbindgen_enum_BinaryType[arg1]; - }, - __wbg_set_capture_6a782955ea62ac61: function(arg0, arg1) { - arg0.capture = arg1 !== 0; - }, - __wbg_set_cookie_b230bb282b0c6f43: function() { return handleError(function (arg0, arg1, arg2) { - arg0.cookie = getStringFromWasm0(arg1, arg2); - }, arguments); }, - __wbg_set_innerHTML_a3c82996073b31ea: function(arg0, arg1, arg2) { - arg0.innerHTML = getStringFromWasm0(arg1, arg2); - }, - __wbg_set_nodeValue_f39ed00fc286b285: function(arg0, arg1, arg2) { - arg0.nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2); - }, - __wbg_set_once_e747a93482f65a72: function(arg0, arg1) { - arg0.once = arg1 !== 0; - }, - __wbg_set_onclose_17fa3bbcc4ba3541: function(arg0, arg1) { - arg0.onclose = arg1; - }, - __wbg_set_onerror_da99c4232662a084: function(arg0, arg1) { - arg0.onerror = arg1; - }, - __wbg_set_onloadend_353e1e891cf72c27: function(arg0, arg1) { - arg0.onloadend = arg1; - }, - __wbg_set_onmessage_c1db358b9c38e3f1: function(arg0, arg1) { - arg0.onmessage = arg1; - }, - __wbg_set_onopen_cd47b8fb1d92dee9: function(arg0, arg1) { - arg0.onopen = arg1; - }, - __wbg_set_passive_69f5c7d4e21e69c3: function(arg0, arg1) { - arg0.passive = arg1 !== 0; - }, - __wbg_set_type_e10300c35573ac85: function(arg0, arg1) { - arg0.type = __wbindgen_enum_OscillatorType[arg1]; - }, - __wbg_set_value_701931da23e8bae7: function(arg0, arg1) { - arg0.value = arg1; - }, - __wbg_slice_5fffd132e3ff5262: function(arg0, arg1) { - const ret = arg1.slice(); - const ptr1 = passArrayJsValueToWasm0(ret, wasm.__wbindgen_malloc); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, - __wbg_start_ccf4b52f03e06e0a: function() { return handleError(function (arg0, arg1) { - arg0.start(arg1); - }, arguments); }, - __wbg_static_accessor_CREATE_TASK_f3ab6a6954bda493: function() { - const ret = typeof console === 'undefined' ? null : console?.createTask; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_static_accessor_GLOBAL_8cfadc87a297ca02: function() { - const ret = typeof global === 'undefined' ? null : global; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_static_accessor_GLOBAL_THIS_602256ae5c8f42cf: function() { - const ret = typeof globalThis === 'undefined' ? null : globalThis; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_static_accessor_SELF_e445c1c7484aecc3: function() { - const ret = typeof self === 'undefined' ? null : self; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_static_accessor_WINDOW_f20e8576ef1e0f17: function() { - const ret = typeof window === 'undefined' ? null : window; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_stopPropagation_e088fca8231e68c4: function(arg0) { - arg0.stopPropagation(); - }, - __wbg_stop_b0230bbe32583fd2: function() { return handleError(function (arg0, arg1) { - arg0.stop(arg1); - }, arguments); }, - __wbg_target_6d97e221d11b71b6: function(arg0) { - const ret = arg0.target; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_textContent_1f28330a124ec047: function(arg0, arg1) { - const ret = arg1.textContent; - var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, - __wbg_then_8e16ee11f05e4827: function(arg0, arg1) { - const ret = arg0.then(arg1); - return ret; - }, - __wbg_toString_6dc1a94e0bdba378: function(arg0) { - const ret = arg0.toString(); - return ret; - }, - __wbg_value_6079dd28568d83c9: function(arg0, arg1) { - const ret = arg1.value; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); - getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); - }, - __wbg_view_701664ffb3b1ce67: function(arg0) { - const ret = arg0.view; - return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); - }, - __wbg_warn_3cc416af27dbdc02: function(arg0) { - console.warn(arg0); - }, - __wbg_warn_bd0f407277b102f4: function(arg0, arg1, arg2) { - console.warn(arg0, arg1, arg2); - }, - __wbindgen_cast_0000000000000001: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1089, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h631ca3ab15db92ac); - return ret; - }, - __wbindgen_cast_0000000000000002: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1146, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__heaa42c2d614740c5); - return ret; - }, - __wbindgen_cast_0000000000000003: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 987, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h6c85d08fbcdf5b5a); - return ret; - }, - __wbindgen_cast_0000000000000004: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("ErrorEvent")], shim_idx: 985, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h8604f84d7f7e1085); - return ret; - }, - __wbindgen_cast_0000000000000005: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1090, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hdb75aae9aec73406); - return ret; - }, - __wbindgen_cast_0000000000000006: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 988, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0ba230b615670f92); - return ret; - }, - __wbindgen_cast_0000000000000007: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("ProgressEvent")], shim_idx: 986, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h662b7d6967267d18); - return ret; - }, - __wbindgen_cast_0000000000000008: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1024, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3420984fa2fe04e3); - return ret; - }, - __wbindgen_cast_0000000000000009: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 965, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. - const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd6fed3fae7865fee); - return ret; - }, - __wbindgen_cast_000000000000000a: function(arg0, arg1) { - // Cast intrinsic for `Ref(String) -> Externref`. - const ret = getStringFromWasm0(arg0, arg1); - return ret; - }, - __wbindgen_init_externref_table: function() { - const table = wasm.__wbindgen_externrefs; - const offset = table.grow(4); - table.set(0, undefined); - table.set(offset + 0, undefined); - table.set(offset + 1, null); - table.set(offset + 2, true); - table.set(offset + 3, false); - }, - }; - return { - __proto__: null, - "./client_web_bg.js": import0, - }; -} - -const lAudioContext = (typeof AudioContext !== 'undefined' ? AudioContext : (typeof webkitAudioContext !== 'undefined' ? webkitAudioContext : undefined)); -function wasm_bindgen__convert__closures_____invoke__h3420984fa2fe04e3(arg0, arg1) { - wasm.wasm_bindgen__convert__closures_____invoke__h3420984fa2fe04e3(arg0, arg1); -} - -function wasm_bindgen__convert__closures_____invoke__hd6fed3fae7865fee(arg0, arg1) { - wasm.wasm_bindgen__convert__closures_____invoke__hd6fed3fae7865fee(arg0, arg1); -} - -function wasm_bindgen__convert__closures_____invoke__hf0f977607302985a(arg0, arg1) { - const ret = wasm.wasm_bindgen__convert__closures_____invoke__hf0f977607302985a(arg0, arg1); - return ret !== 0; -} - -function wasm_bindgen__convert__closures_____invoke__h631ca3ab15db92ac(arg0, arg1, arg2) { - wasm.wasm_bindgen__convert__closures_____invoke__h631ca3ab15db92ac(arg0, arg1, arg2); -} - -function wasm_bindgen__convert__closures_____invoke__h6c85d08fbcdf5b5a(arg0, arg1, arg2) { - wasm.wasm_bindgen__convert__closures_____invoke__h6c85d08fbcdf5b5a(arg0, arg1, arg2); -} - -function wasm_bindgen__convert__closures_____invoke__h8604f84d7f7e1085(arg0, arg1, arg2) { - wasm.wasm_bindgen__convert__closures_____invoke__h8604f84d7f7e1085(arg0, arg1, arg2); -} - -function wasm_bindgen__convert__closures_____invoke__hdb75aae9aec73406(arg0, arg1, arg2) { - wasm.wasm_bindgen__convert__closures_____invoke__hdb75aae9aec73406(arg0, arg1, arg2); -} - -function wasm_bindgen__convert__closures_____invoke__h0ba230b615670f92(arg0, arg1, arg2) { - wasm.wasm_bindgen__convert__closures_____invoke__h0ba230b615670f92(arg0, arg1, arg2); -} - -function wasm_bindgen__convert__closures_____invoke__h662b7d6967267d18(arg0, arg1, arg2) { - wasm.wasm_bindgen__convert__closures_____invoke__h662b7d6967267d18(arg0, arg1, arg2); -} - -function wasm_bindgen__convert__closures_____invoke__heaa42c2d614740c5(arg0, arg1, arg2) { - const ret = wasm.wasm_bindgen__convert__closures_____invoke__heaa42c2d614740c5(arg0, arg1, arg2); - if (ret[1]) { - throw takeFromExternrefTable0(ret[0]); - } -} - -function wasm_bindgen__convert__closures_____invoke__h42c7f62e44be4353(arg0, arg1, arg2, arg3) { - wasm.wasm_bindgen__convert__closures_____invoke__h42c7f62e44be4353(arg0, arg1, arg2, arg3); -} - - -const __wbindgen_enum_BinaryType = ["blob", "arraybuffer"]; - - -const __wbindgen_enum_OscillatorType = ["sine", "square", "sawtooth", "triangle", "custom"]; - - -const __wbindgen_enum_ReadableStreamType = ["bytes"]; -const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0, 1)); -const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0, 1)); -const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0, 1)); - -function addToExternrefTable0(obj) { - const idx = wasm.__externref_table_alloc(); - wasm.__wbindgen_externrefs.set(idx, obj); - return idx; -} - -const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(state => wasm.__wbindgen_destroy_closure(state.a, state.b)); - -function debugString(val) { - // primitive types - const type = typeof val; - if (type == 'number' || type == 'boolean' || val == null) { - return `${val}`; - } - if (type == 'string') { - return `"${val}"`; - } - if (type == 'symbol') { - const description = val.description; - if (description == null) { - return 'Symbol'; - } else { - return `Symbol(${description})`; - } - } - if (type == 'function') { - const name = val.name; - if (typeof name == 'string' && name.length > 0) { - return `Function(${name})`; - } else { - return 'Function'; - } - } - // objects - if (Array.isArray(val)) { - const length = val.length; - let debug = '['; - if (length > 0) { - debug += debugString(val[0]); - } - for(let i = 1; i < length; i++) { - debug += ', ' + debugString(val[i]); - } - debug += ']'; - return debug; - } - // Test for built-in - const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); - let className; - if (builtInMatches && builtInMatches.length > 1) { - className = builtInMatches[1]; - } else { - // Failed to match the standard '[object ClassName]' - return toString.call(val); - } - if (className == 'Object') { - // we're a user defined class or Object - // JSON.stringify avoids problems with cycles, and is generally much - // easier than looping through ownProperties of `val`. - try { - return 'Object(' + JSON.stringify(val) + ')'; - } catch (_) { - return 'Object'; - } - } - // errors - if (val instanceof Error) { - return `${val.name}: ${val.message}\n${val.stack}`; - } - // TODO we could test for more things here, like `Set`s and `Map`s. - return className; -} - -function getArrayU8FromWasm0(ptr, len) { - ptr = ptr >>> 0; - return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); -} - -let cachedDataViewMemory0 = null; -function getDataViewMemory0() { - if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { - cachedDataViewMemory0 = new DataView(wasm.memory.buffer); - } - return cachedDataViewMemory0; -} - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return decodeText(ptr, len); -} - -let cachedUint8ArrayMemory0 = null; -function getUint8ArrayMemory0() { - if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { - cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8ArrayMemory0; -} - -function handleError(f, args) { - try { - return f.apply(this, args); - } catch (e) { - const idx = addToExternrefTable0(e); - wasm.__wbindgen_exn_store(idx); - } -} - -function isLikeNone(x) { - return x === undefined || x === null; -} - -function makeMutClosure(arg0, arg1, f) { - const state = { a: arg0, b: arg1, cnt: 1 }; - const real = (...args) => { - - // First up with a closure we increment the internal reference - // count. This ensures that the Rust closure environment won't - // be deallocated while we're invoking it. - state.cnt++; - const a = state.a; - state.a = 0; - try { - return f(a, state.b, ...args); - } finally { - state.a = a; - real._wbg_cb_unref(); - } - }; - real._wbg_cb_unref = () => { - if (--state.cnt === 0) { - wasm.__wbindgen_destroy_closure(state.a, state.b); - state.a = 0; - CLOSURE_DTORS.unregister(state); - } - }; - CLOSURE_DTORS.register(real, state, state); - return real; -} - -function passArrayJsValueToWasm0(array, malloc) { - const ptr = malloc(array.length * 4, 4) >>> 0; - for (let i = 0; i < array.length; i++) { - const add = addToExternrefTable0(array[i]); - getDataViewMemory0().setUint32(ptr + 4 * i, add, true); - } - WASM_VECTOR_LEN = array.length; - return ptr; -} - -function passStringToWasm0(arg, malloc, realloc) { - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length, 1) >>> 0; - getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len, 1) >>> 0; - - const mem = getUint8ArrayMemory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; - const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); - const ret = cachedTextEncoder.encodeInto(arg, view); - - offset += ret.written; - ptr = realloc(ptr, len, offset, 1) >>> 0; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -function takeFromExternrefTable0(idx) { - const value = wasm.__wbindgen_externrefs.get(idx); - wasm.__externref_table_dealloc(idx); - return value; -} - -let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); -cachedTextDecoder.decode(); -const MAX_SAFARI_DECODE_BYTES = 2146435072; -let numBytesDecoded = 0; -function decodeText(ptr, len) { - numBytesDecoded += len; - if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) { - cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - cachedTextDecoder.decode(); - numBytesDecoded = len; - } - return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); -} - -const cachedTextEncoder = new TextEncoder(); - -if (!('encodeInto' in cachedTextEncoder)) { - cachedTextEncoder.encodeInto = function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; - }; -} - -let WASM_VECTOR_LEN = 0; - -let wasmModule, wasm; -function __wbg_finalize_init(instance, module) { - wasm = instance.exports; - wasmModule = module; - cachedDataViewMemory0 = null; - cachedUint8ArrayMemory0 = null; - wasm.__wbindgen_start(); - return wasm; -} - -async function __wbg_load(module, imports) { - if (typeof Response === 'function' && module instanceof Response) { - if (typeof WebAssembly.instantiateStreaming === 'function') { - try { - return await WebAssembly.instantiateStreaming(module, imports); - } catch (e) { - const validResponse = module.ok && expectedResponseType(module.type); - - if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - - } else { throw e; } - } - } - - const bytes = await module.arrayBuffer(); - return await WebAssembly.instantiate(bytes, imports); - } else { - const instance = await WebAssembly.instantiate(module, imports); - - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - } else { - return instance; - } - } - - function expectedResponseType(type) { - switch (type) { - case 'basic': case 'cors': case 'default': return true; - } - return false; - } -} - -function initSync(module) { - if (wasm !== undefined) return wasm; - - - if (module !== undefined) { - if (Object.getPrototypeOf(module) === Object.prototype) { - ({module} = module) - } else { - console.warn('using deprecated parameters for `initSync()`; pass a single object instead') - } - } - - const imports = __wbg_get_imports(); - if (!(module instanceof WebAssembly.Module)) { - module = new WebAssembly.Module(module); - } - const instance = new WebAssembly.Instance(module, imports); - return __wbg_finalize_init(instance, module); -} - -async function __wbg_init(module_or_path) { - if (wasm !== undefined) return wasm; - - - if (module_or_path !== undefined) { - if (Object.getPrototypeOf(module_or_path) === Object.prototype) { - ({module_or_path} = module_or_path) - } else { - console.warn('using deprecated parameters for the initialization function; pass a single object instead') - } - } - - if (module_or_path === undefined) { - module_or_path = new URL('client_web_bg.wasm', import.meta.url); - } - const imports = __wbg_get_imports(); - - if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { - module_or_path = fetch(module_or_path); - } - - const { instance, module } = await __wbg_load(await module_or_path, imports); - - return __wbg_finalize_init(instance, module); -} - -export { initSync, __wbg_init as default }; diff --git a/clients/web-game/dist/client_web-4248a2b78bb5a03_bg.wasm b/clients/web-game/dist/client_web-4248a2b78bb5a03_bg.wasm deleted file mode 100644 index e3117d4..0000000 Binary files a/clients/web-game/dist/client_web-4248a2b78bb5a03_bg.wasm and /dev/null differ diff --git a/clients/web-game/dist/style-398501cc5e039e60.css b/clients/web-game/dist/style-398501cc5e039e60.css deleted file mode 100644 index 3691894..0000000 --- a/clients/web-game/dist/style-398501cc5e039e60.css +++ /dev/null @@ -1,1133 +0,0 @@ -/* ── Google Fonts ───────────────────────────────────────────────────── */ -@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Jost:wght@300;400;500&display=swap'); - -/* ── Design tokens ──────────────────────────────────────────────────── */ -:root { - --board-felt: #1d3d28; - --board-rail: #2a1508; - --field-ivory: #f0e6c8; - --field-burgundy: #7a1e2a; - --field-corner: #b8900a; - --checker-white: #f5edd8; - --checker-black: #1a0f06; - --checker-ring: #c8a448; - --ui-parchment: #f2e8d0; - --ui-parchment-dark: #e4d8b8; - --ui-ink: #2a1a08; - --ui-gold: #c8a448; - --ui-gold-dark: #8a6a28; - --ui-green-accent: #3a6b2a; - --ui-red-accent: #7a1e2a; - --font-display: 'Cormorant Garamond', Georgia, serif; - --font-ui: 'Jost', system-ui, sans-serif; -} - -/* ── Reset & base ──────────────────────────────────────────────────── */ -*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } - -body { - font-family: var(--font-ui); - background: #8a7050; - background-image: - radial-gradient(ellipse at 20% 10%, rgba(80,48,16,0.35) 0%, transparent 60%), - radial-gradient(ellipse at 80% 90%, rgba(40,24,8,0.3) 0%, transparent 55%), - repeating-linear-gradient( - 45deg, transparent, transparent 3px, - rgba(0,0,0,0.03) 3px, rgba(0,0,0,0.03) 4px - ); - display: flex; - justify-content: center; - padding: 1.5rem; - min-height: 100vh; -} - -.hidden { display: none !important; } - -/* ── Login card (§11) ───────────────────────────────────────────────── */ -.login-card { - width: 340px; - margin-top: 5vh; - border-radius: 8px; - overflow: hidden; - box-shadow: - 0 20px 60px rgba(0,0,0,0.55), - 0 0 0 1px rgba(200,164,72,0.35), - 0 0 0 5px rgba(42,21,8,0.9), - 0 0 0 6px rgba(200,164,72,0.2); - background: var(--ui-parchment); -} - -/* Decorative header — row of triangular flèches like the actual board */ -.login-card-header { - height: 52px; - background: var(--board-felt); - position: relative; - overflow: hidden; -} - -.login-board-stripe { - position: absolute; - inset: 0; - /* Alternating burgundy/ivory triangles pointing down from the top */ - background: - repeating-linear-gradient( - 90deg, - var(--field-burgundy) 0, var(--field-burgundy) 50%, - var(--field-ivory) 50%, var(--field-ivory) 100% - ); - background-size: 34px 100%; - /* Clip into downward-pointing triangles */ - clip-path: polygon( - 0% 0%, 2.94% 100%, 5.88% 0%, 8.82% 100%, 11.76% 0%, - 14.7% 100%, 17.65% 0%, 20.59% 100%, 23.53% 0%, - 26.47% 100%, 29.41% 0%, 32.35% 100%, 35.29% 0%, - 38.24% 100%, 41.18% 0%, 44.12% 100%, 47.06% 0%, - 50% 100%, 52.94% 0%, 55.88% 100%, 58.82% 0%, - 61.76% 100%, 64.71% 0%, 67.65% 100%, 70.59% 0%, - 73.53% 100%, 76.47% 0%, 79.41% 100%, 82.35% 0%, - 85.29% 100%, 88.24% 0%, 91.18% 100%, 94.12% 0%, - 97.06% 100%, 100% 0% - ); - opacity: 0.9; -} - -.login-card-body { - display: flex; - flex-direction: column; - align-items: center; - gap: 0; - padding: 1.5rem 2rem 2rem; -} - -.login-lang-switcher { - align-self: flex-end; - margin-bottom: 0.75rem; -} - -/* Override lang-switcher colours for the parchment card */ -.login-card .lang-switcher button { - color: var(--ui-ink); - border-color: rgba(42,21,8,0.2); - opacity: 0.5; -} -.login-card .lang-switcher button.lang-active { - opacity: 1; - background: rgba(42,21,8,0.08); - border-color: rgba(42,21,8,0.35); -} - -.login-title { - font-family: var(--font-display); - font-size: 3.25rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.12em; - text-align: center; - line-height: 1; - margin-bottom: 0.3rem; -} - -.login-subtitle { - font-family: var(--font-display); - font-size: 0.85rem; - color: rgba(42,26,8,0.55); - text-align: center; - letter-spacing: 0.06em; - font-style: italic; - margin-bottom: 1.25rem; -} -.login-subtitle sup { - font-size: 0.65em; - vertical-align: super; -} - -.login-ornament { - color: var(--ui-gold); - font-size: 1rem; - opacity: 0.7; - margin-bottom: 1.25rem; - letter-spacing: 0.3em; - text-align: center; -} - -.error-msg { - color: #c03030; - font-size: 0.85rem; - text-align: center; - margin-bottom: 0.5rem; -} - -.login-input { - width: 100%; - padding: 0.55rem 0.85rem; - font-size: 0.95rem; - font-family: var(--font-ui); - border: 1px solid rgba(138,106,40,0.4); - border-radius: 5px; - background: rgba(255,252,240,0.8); - color: var(--ui-ink); - outline: none; - transition: border-color 0.15s, box-shadow 0.15s; - margin-bottom: 1rem; -} -.login-input:focus { - border-color: var(--ui-gold); - box-shadow: 0 0 0 3px rgba(200,164,72,0.2); -} - -.login-actions { - display: flex; - flex-direction: column; - gap: 0.55rem; - width: 100%; -} - -/* Login buttons styled as embossed wooden tiles */ -.login-btn { - width: 100%; - padding: 0.65rem 1rem; - font-family: var(--font-ui); - font-size: 0.9rem; - font-weight: 500; - letter-spacing: 0.04em; - border: none; - border-radius: 5px; - cursor: pointer; - transition: opacity 0.15s, transform 0.1s, box-shadow 0.15s; - position: relative; -} -.login-btn:disabled { opacity: 0.35; cursor: default; } -.login-btn:not(:disabled):hover { opacity: 0.92; transform: translateY(-1px); } -.login-btn:not(:disabled):active { transform: translateY(0); } - -.login-btn-primary { - background: linear-gradient(160deg, #4a7a38 0%, #2e5222 100%); - color: #e8f0e0; - box-shadow: 0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.12); -} -.login-btn-secondary { - background: linear-gradient(160deg, #3a2010 0%, #241408 100%); - color: #e4d4b4; - box-shadow: 0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.08); -} -.login-btn-bot { - background: linear-gradient(160deg, #2a4a6a 0%, #183050 100%); - color: #d0e0f0; - box-shadow: 0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.08); -} - -/* ── Connecting screen ──────────────────────────────────────────────── */ -.connecting { - font-family: var(--font-display); - font-size: 1.4rem; - font-style: italic; - margin-top: 4rem; - text-align: center; - color: var(--ui-parchment); - text-shadow: 0 1px 4px rgba(0,0,0,0.4); -} - -/* ── Game-action buttons ─────────────────────────────────────────────── */ -.btn { - padding: 0.5rem 1.25rem; - font-size: 0.95rem; - font-family: var(--font-ui); - font-weight: 500; - letter-spacing: 0.03em; - border: none; - border-radius: 4px; - cursor: pointer; - transition: opacity 0.15s, box-shadow 0.15s; -} -.btn:disabled { opacity: 0.4; cursor: default; } -.btn-primary { background: var(--ui-green-accent); color: #fff; } -.btn-secondary { background: var(--board-rail); color: #e8d8b8; } -.btn-bot { background: #2a5a7a; color: #fff; } -.btn:not(:disabled):hover { - opacity: 0.9; - box-shadow: 0 2px 6px rgba(0,0,0,0.25); -} - -/* ── Game container ─────────────────────────────────────────────────── */ -/* No width: 100% — let it size to content (the board wrapper, ~832px). - This keeps the board pinned at the same horizontal position whether or - not the side panel is visible, and aligns the status bar / score panels - with the board rather than with the viewport edge. */ -.game-container { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.6rem; -} - -/* ── Language switcher (in-game) ────────────────────────────────────── */ -.lang-switcher { display: flex; gap: 0.25rem; } - -.lang-switcher button { - font-size: 0.7rem; - font-family: var(--font-ui); - letter-spacing: 0.05em; - padding: 0.15rem 0.4rem; - border: 1px solid rgba(200,164,72,0.3); - border-radius: 3px; - background: transparent; - cursor: pointer; - color: var(--ui-parchment); - opacity: 0.55; -} -.lang-switcher button.lang-active { - opacity: 1; - font-weight: 500; - background: rgba(200,164,72,0.15); - border-color: rgba(200,164,72,0.6); -} - -/* ── Top bar ─────────────────────────────────────────────────────────── */ -.top-bar { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - color: var(--ui-parchment); - font-size: 0.85rem; - opacity: 0.8; -} - -.quit-link { - font-size: 0.8rem; - color: var(--ui-parchment); - text-decoration: underline; - text-underline-offset: 2px; - cursor: pointer; - opacity: 0.7; -} -.quit-link:hover { opacity: 1; } - -/* ── Game status bar (§10b) — above board ───────────────────────────── */ -.game-status { - font-family: var(--font-display); - font-size: 1.2rem; - font-style: italic; - color: var(--ui-parchment); - text-align: center; - letter-spacing: 0.04em; - padding: 0.2rem 1rem 0; - width: 100%; - text-shadow: 0 1px 4px rgba(0,0,0,0.4); -} - -/* ── Contextual sub-prompt (§8a) ────────────────────────────────────── */ -.game-sub-prompt { - font-family: var(--font-ui); - font-size: 0.72rem; - color: rgba(240,228,192,0.5); - text-align: center; - letter-spacing: 0.04em; - padding: 0.15rem 1rem 0; - width: 100%; -} - -/* ── Player score panel ─────────────────────────────────────────────── */ -/* Horizontal banner: name on the left, score bars expanding to fill the - board width — no more empty right half on large screens. */ -.player-score-panel { - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.45rem 1.25rem; - font-size: 0.88rem; - box-shadow: 0 2px 6px rgba(0,0,0,0.25); - width: 100%; - border-top: 2px solid var(--ui-gold-dark); - display: flex; - align-items: center; - gap: 1.5rem; -} - -.player-score-header { - flex-shrink: 0; - min-width: 90px; -} - -.player-name { - font-family: var(--font-display); - font-weight: 600; - font-size: 1.05rem; - color: var(--ui-ink); - letter-spacing: 0.02em; -} - -/* Bars sit side-by-side (points | holes) filling remaining width */ -.score-bars { display: flex; flex-direction: row; gap: 1.5rem; flex: 1; align-items: center; } - -.score-bar-row { - display: flex; - align-items: center; - gap: 0.5rem; - flex: 1; -} - -.score-bar-label { - font-size: 0.75rem; - color: #665544; - width: 3rem; - text-align: right; - flex-shrink: 0; -} - -/* ── Points bar ─────────────────────────────────────────────────────── */ -.score-bar { - flex: 1; - max-width: 220px; - height: 8px; - background: rgba(0,0,0,0.1); - border-radius: 4px; - overflow: hidden; - flex-shrink: 0; -} - -.score-bar-fill { - height: 100%; - border-radius: 4px; - transition: width 0.35s ease-out; -} - -.score-bar-points { background: linear-gradient(90deg, var(--ui-green-accent), #5a9b3a); } - -.score-bar-value { - font-size: 0.75rem; - color: #665544; - min-width: 2.5rem; - font-variant-numeric: tabular-nums; -} - -/* ── Hole peg tracker (§7a) ─────────────────────────────────────────── */ -.peg-track { - display: flex; - align-items: center; - gap: 3px; - flex-shrink: 0; -} - -.peg-hole { - width: 10px; - height: 10px; - border-radius: 50%; - border: 1.5px solid rgba(138,106,40,0.45); - background: rgba(0,0,0,0.06); - flex-shrink: 0; - transition: background 0.3s ease-out, border-color 0.3s, box-shadow 0.3s; -} - -.peg-hole.filled { - background: var(--ui-gold); - border-color: var(--ui-gold-dark); - box-shadow: 0 0 4px rgba(200,164,72,0.6); -} - -.bredouille-badge { - font-size: 0.62rem; - font-weight: 500; - color: #fff8e0; - background: linear-gradient(135deg, #c88800, #8a5800); - border: 1px solid rgba(200,164,72,0.5); - border-radius: 3px; - padding: 0.1em 0.4em; - letter-spacing: 0.06em; - cursor: default; - box-shadow: 0 1px 3px rgba(0,0,0,0.25); -} - -/* ── Board + side panel ─────────────────────────────────────────────── */ -/* .board-and-panel is sized to the board wrapper only; the side panel is - positioned absolutely so it floats to the right without pushing the - board and breaking its horizontal alignment. */ -.board-and-panel { - position: relative; -} - -/* The side panel is anchored to the board's RIGHT edge. Scoring panel - wrappers inside it initially overlap the board; they slide to a peek - strip after a few seconds, and reveal fully on hover. */ -.side-panel { - position: absolute; - right: -8px; - top: 10px; - z-index: 20; - display: flex; - flex-direction: column; - gap: 0.5rem; - padding-top: 0.15rem; - pointer-events: none; /* pass board clicks through the empty area */ -} - -.action-buttons { display: flex; flex-direction: column; gap: 0.5rem; } - -/* ── Dice bar ───────────────────────────────────────────────────────── */ -.dice-bar { - display: flex; - align-items: center; - gap: 0.6rem; - padding: 0.4rem 0.6rem; - background: rgba(42,21,8,0.15); - border-radius: 6px; - border: 1px solid rgba(200,164,72,0.2); - width: fit-content; -} - -/* ── Die face (SVG) ─────────────────────────────────────────────────── */ - -/* §5a — vigorous tumble: die bounces in from a random rotation */ -@keyframes die-tumble { - 0% { transform: rotate(-45deg) scale(0.4) translateY(-8px); opacity: 0; } - 25% { transform: rotate(18deg) scale(1.22) translateY(0); opacity: 1; } - 45% { transform: rotate(-10deg) scale(0.91); } - 62% { transform: rotate(6deg) scale(1.06); } - 76% { transform: rotate(-3deg) scale(0.98); } - 88% { transform: rotate(1.5deg) scale(1.01); } - 100% { transform: rotate(0deg) scale(1); opacity: 1; } -} - -.die-face { - filter: drop-shadow(0 2px 3px rgba(0,0,0,0.3)); - animation: die-tumble 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) both; -} - -.die-face rect { - fill: #fffef0; - stroke: #2a1a00; - stroke-width: 2; - transition: fill 0.18s, stroke 0.18s; -} -.die-face circle { - fill: #1a0a00; - transition: fill 0.18s; -} - -/* Bar die slot — centered in the board bar */ -.bar-die-slot { - display: flex; - align-items: center; - justify-content: center; -} - -/* Double glow (§5c) */ -.die-face.die-double rect { stroke: var(--ui-gold); stroke-width: 2.5; } -.die-face.die-double { - filter: drop-shadow(0 0 6px rgba(200,164,72,0.7)) drop-shadow(0 2px 3px rgba(0,0,0,0.3)); -} - -.die-face.die-used { animation: none; opacity: 0.55; } -.die-face.die-used rect { fill: #d4d0c4; stroke: #9a8a70; } -.die-face.die-used circle { fill: #9a8a70; } - -/* ── Jan panel ──────────────────────────────────────────────────────── */ -.jan-panel { - display: flex; - flex-direction: column; - gap: 2px; - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.4rem 0.9rem; - font-size: 0.88rem; - box-shadow: 0 1px 4px rgba(0,0,0,0.15); - min-width: 260px; - border-top: 2px solid rgba(138,106,40,0.35); -} - -.jan-row { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 2px 4px; - border-radius: 3px; -} -.jan-expandable { cursor: pointer; } -.jan-expandable:hover { background: rgba(0,0,0,0.05); } - -.jan-positive { color: #1a5c1a; } -.jan-negative { color: #8b1a1a; } - -.jan-label { flex: 1; } -.jan-tag { - font-size: 0.72rem; - padding: 0.1em 0.4em; - border-radius: 3px; - background: rgba(0,0,0,0.07); - color: #665544; - white-space: nowrap; -} -.jan-pts { font-weight: 600; text-align: right; min-width: 3rem; } - -.jan-moves { padding: 1px 4px 4px 1rem; display: flex; flex-direction: column; gap: 2px; } -.jan-moves.hidden { display: none; } -.jan-move-line { font-family: monospace; font-size: 0.78rem; color: #555; } - -/* ── Game-over overlay (§12) ────────────────────────────────────────── */ -.game-over-overlay { - position: fixed; - inset: 0; - background: rgba(0,0,0,0.65); - display: flex; - align-items: center; - justify-content: center; - z-index: 100; -} - -@keyframes game-over-appear { - from { transform: translateY(-24px) scale(0.94); opacity: 0; } - to { transform: translateY(0) scale(1); opacity: 1; } -} - -.game-over-box { - background: var(--ui-parchment); - border-radius: 8px; - padding: 2.5rem 3rem; - text-align: center; - box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 0 2px var(--ui-gold-dark); - display: flex; - flex-direction: column; - gap: 1.1rem; - min-width: 300px; - animation: game-over-appear 0.4s cubic-bezier(0.22, 0.61, 0.36, 1); -} - -.game-over-box h2 { - font-family: var(--font-display); - font-size: 2rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.06em; -} - -.game-over-winner { - font-family: var(--font-display); - font-size: 1.25rem; - color: var(--ui-green-accent); - font-style: italic; -} - -/* Final score ledger */ -.game-over-score { - display: flex; - align-items: center; - justify-content: center; - gap: 0.75rem; - padding: 0.6rem 1rem; - background: rgba(0,0,0,0.05); - border-radius: 5px; - border: 1px solid rgba(138,106,40,0.2); -} - -.game-over-score-name { - font-family: var(--font-display); - font-size: 0.9rem; - color: #665544; - font-style: italic; - max-width: 80px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.game-over-score-nums { - font-family: var(--font-display); - font-size: 2.25rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.08em; - line-height: 1; -} - -.game-over-actions { display: flex; gap: 0.75rem; justify-content: center; } - -/* ── Scoring notification panel (§6b) ───────────────────────────────── */ - -/* ── Wrapper: handles slide-in → peek → reveal lifecycle ────────────── - The wrapper starts off-screen right (translateX(100%)), slides in on - mount via animation, then Leptos adds .peeked after 3.4s to slide it - back to a 28px peek strip. */ -@keyframes scoring-panel-enter { - from { transform: translateX(100%); } - to { transform: translateX(0); } -} - -.scoring-panel-wrapper { - /* width: 290px; */ - pointer-events: auto; - animation: scoring-panel-enter 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94); - transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); - filter: drop-shadow(-4px 0 14px rgba(0,0,0,0.38)); -} - -/* Peeked: slide right by the full panel width so the board is 100% clear. - The panel's left portion stays visible in whatever free space exists to - the right of the board. */ -.scoring-panel-wrapper.peeked { - transform: translateX(100%); -} - -/* Click on the visible left strip → .revealed slides it back over the board. - A second click removes .revealed and returns to the peeked position. */ -.scoring-panel-wrapper.revealed { - transform: translateX(0); -} - -/* Pointer cursor on the peeked (clickable) strip */ -.scoring-panel-wrapper.peeked:not(.revealed) { - cursor: pointer; -} - -/* ── Inner panel card ─────────────────────────────────────────────────── */ -.scoring-panel { - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.45rem 0.85rem; - font-size: 0.84rem; - box-shadow: 0 1px 4px rgba(0,0,0,0.15); - border-left: 3px solid var(--ui-green-accent); - display: flex; - flex-direction: column; - gap: 4px; - width: 100%; -} - -.scoring-total { - font-family: var(--font-display); - font-weight: 600; - font-size: 1rem; - color: #1a5c1a; - white-space: nowrap; -} - -.scoring-jan-row { - display: flex; - align-items: center; - gap: 0.4rem; - padding: 2px 3px; - border-radius: 3px; - cursor: default; - white-space: nowrap; -} -.scoring-jan-row:hover { background: rgba(0,0,0,0.05); } - -.scoring-panel-opp { border-left-color: var(--board-rail); } -.scoring-panel-opp .scoring-total { color: var(--ui-red-accent); } - -.scoring-hole { - display: flex; - align-items: center; - gap: 0.4rem; - font-weight: 600; - color: var(--ui-red-accent); - margin-top: 3px; - padding-top: 4px; - border-top: 1px solid rgba(0,0,0,0.1); -} - -.hold-go-buttons { display: flex; gap: 0.5rem; margin-top: 4px; } - -/* ── Large-screen layout: panel in free space, no peek needed ───────── - Threshold: board (832) + body-padding (48) + panel-gap (16) + panel (290) - + symmetric left margin = 1492 px. - At this width the panel fits entirely to the right of the board. */ -@media (min-width: 1492px) { - .side-panel { - right: auto; - left: calc(100% + 1rem); /* outside board, no overlap */ - } - /* Already fully visible in free space — peeked/revealed are no-ops. */ - .scoring-panel-wrapper.peeked, - .scoring-panel-wrapper.revealed { - transform: none; - cursor: default; - } -} - -/* ── Board wrapper ──────────────────────────────────────────────────── */ -.board-wrapper { - display: flex; - flex-direction: column; - gap: 3px; -} - -/* ── Zone labels (§2a) — aligned with board quarters ───────────────── */ -/* Board border(4) + padding(4) = 8px inset each side */ -.zone-labels-row { - display: flex; - gap: 4px; - padding: 0 8px; -} - -.zone-label { - font-family: var(--font-display); - font-size: 0.57rem; - font-style: italic; - color: rgba(240,228,192,0.48); - letter-spacing: 0.1em; - text-align: center; - pointer-events: none; - line-height: 1; -} - -.zone-label-quarter { width: 370px; flex-shrink: 0; } -.zone-label-bar { width: 68px; flex-shrink: 0; } - -/* ── Board ──────────────────────────────────────────────────────────── */ -.board { - background: var(--board-felt); - border: 4px solid var(--board-rail); - border-radius: 6px; - padding: 4px; - display: flex; - flex-direction: column; - gap: 4px; - user-select: none; - box-shadow: - 0 6px 16px rgba(0,0,0,0.5), - inset 0 1px 0 rgba(255,255,255,0.04); - position: relative; -} - -.board-row { display: flex; gap: 4px; } -.board-quarter { display: flex; gap: 2px; } - -.board-bar { - width: 68px; - background: var(--board-rail); - border-radius: 4px; - box-shadow: inset 0 0 6px rgba(0,0,0,0.5); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - overflow: visible; -} - -.board-center-bar { - height: 12px; - background: var(--board-rail); - border-radius: 2px; - box-shadow: inset 0 0 4px rgba(0,0,0,0.4); -} - -/* ── Fields (§1) ────────────────────────────────────────────────────── */ -/* - * Each field is a transparent rectangle over the felt. - * The triangular flèche is drawn by ::before using clip-path. - * --fc controls the triangle colour; z-index:-1 keeps the triangle - * behind checkers; isolation:isolate confines the negative z-index. - */ -.field { - --fc: var(--field-ivory); /* default triangle colour */ - width: 60px; - height: 180px; - background: transparent; /* felt shows through between triangle tips */ - isolation: isolate; /* stacking context for z-index:-1 ::before */ - border-radius: 3px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: flex-end; - padding: 4px 2px; - position: relative; -} - -/* Bot-row triangle: wide base at bottom, tip at top */ -.field::before { - content: ''; - position: absolute; - inset: 0; - z-index: -1; /* behind checkers & corner crown */ - background: var(--fc); - clip-path: polygon(0% 100%, 50% 0%, 100% 100%); - transition: background 0.12s; -} - -/* Top-row triangle: wide base at top, tip at bottom */ -.top-row .field::before { - clip-path: polygon(0% 0%, 100% 0%, 50% 100%); -} - -.top-row .field { justify-content: flex-start; } - -/* ── Zone alternating colours (§2b) ────────────────────────────────── */ -/* petit-jan and grand-jan: burgundy / ivory */ -.board-quarter .field.zone-petit:nth-child(odd), -.board-quarter .field.zone-grand:nth-child(odd) { --fc: var(--field-burgundy); } -.board-quarter .field.zone-petit:nth-child(even), -.board-quarter .field.zone-grand:nth-child(even) { --fc: var(--field-ivory); } - -/* Opponent's grand-jan — deep slate-blue / silvery-green ivory. - Previously #1e3d32 was nearly identical to the felt (#1d3d28); now using - a clearly distinguishable cool blue that reads well against the green. */ -.board-quarter .field.zone-opponent:nth-child(odd) { --fc: #1a4f72; } -.board-quarter .field.zone-opponent:nth-child(even) { --fc: #e5eadc; } - -/* Jan de retour — warmer: amber-brown / warm amber ivory */ -.board-quarter .field.zone-retour:nth-child(odd) { --fc: #6a2810; } -.board-quarter .field.zone-retour:nth-child(even) { --fc: #f2dfa0; } - -/* ── Rest corner — before .clickable so green wins when interactive ── */ -/* .field.corner { --fc: var(--field-corner) !important; } */ - -/* Crown glyph sits behind checkers (z-index:-1) so it shows only on empty corners */ -.field.corner::after { - content: '♛'; - position: absolute; - z-index: -1; - bottom: 22px; - font-size: 0.7rem; - color: rgba(255,248,210,0.38); - pointer-events: none; - line-height: 1; -} -.top-row .field.corner::after { bottom: auto; top: 22px; } - -/* Corner pulse (§8d) — filter respects the triangle shape */ -@keyframes corner-pulse { - 0%, 100% { filter: drop-shadow(0 0 0px rgba(200,164,72,0)); } - 50% { filter: drop-shadow(0 0 7px rgba(200,164,72,0.55)); } -} -.field.corner.corner-available { - animation: corner-pulse 1.5s ease-in-out infinite; -} - -/* ── Exit-eligible highlight (§8c) — filter glow on triangle ───────── */ -@keyframes exit-glow { - 0%, 100% { filter: drop-shadow(0 0 0px rgba(232,192,96,0)); } - 50% { filter: drop-shadow(0 0 5px rgba(232,192,96,0.5)); } -} -.field.exit-eligible { - animation: exit-glow 2s ease-in-out infinite; -} - -/* ── §6c — Jan hover field highlight ────────────────────────────────── */ -.field.jan-hovered { - --fc: rgba(190, 140, 35, 0.8) !important; -} - -/* ── §6e — Hit (battue) ripple ──────────────────────────────────────── */ -@keyframes hit-ripple { - from { transform: translate(-50%, -50%) scale(0.4); opacity: 0.9; } - to { transform: translate(-50%, -50%) scale(2.2); opacity: 0; } -} -.hit-ripple { - position: absolute; - left: 50%; - width: 36px; - height: 36px; - border-radius: 50%; - border: 2px solid rgba(200, 164, 72, 0.9); - pointer-events: none; - animation: hit-ripple 0.5s ease-out forwards; -} -.hit-ripple-top { top: 26px; } -.hit-ripple-bot { bottom: 26px; } - -/* ── Interactive states — after .corner to take visual priority ─────── */ -.field.clickable { - cursor: pointer; - --fc: #8fc840 !important; -} -.field.clickable:hover { --fc: #74aa28 !important; } -.field.selected { - --fc: #5a8a18 !important; - outline: 2px solid rgba(255,255,255,0.3); - outline-offset: -2px; -} - -/* ── Field numbers ──────────────────────────────────────────────────── */ -.field-num { - font-size: 0.58rem; - color: rgba(0,0,0,0.28); - position: absolute; - bottom: 3px; - line-height: 1; - font-variant-numeric: tabular-nums; -} - -.board-quarter .field.zone-petit:nth-child(odd) .field-num, -.board-quarter .field.zone-grand:nth-child(odd) .field-num, -.board-quarter .field.zone-retour:nth-child(odd) .field-num, -.board-quarter .field.zone-opponent:nth-child(odd) .field-num { - color: rgba(240,215,190,0.38); -} - -.field.corner .field-num { color: rgba(255,248,200,0.4); } - -.top-row .field-num { bottom: auto; top: 3px; } - -/* ── Checkers ───────────────────────────────────────────────────────── */ -.checker-stack { - display: flex; - flex-direction: column; - align-items: center; -} - -.checker { - width: 40px; - height: 40px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.78rem; - font-weight: 600; - flex-shrink: 0; -} - -.checker + .checker { margin-top: -4px; } - -.checker.white { - background-image: - radial-gradient(ellipse 50% 35% at 36% 30%, - rgba(255,255,255,0.65) 0%, transparent 100%), - radial-gradient(circle, - transparent 68%, rgba(160,130,70,0.22) 68.5%, - rgba(160,130,70,0.22) 71.5%, transparent 72%), - radial-gradient(circle, - transparent 43%, rgba(160,130,70,0.17) 43.5%, - rgba(160,130,70,0.17) 46.5%, transparent 47%), - radial-gradient(circle at 38% 32%, - #ffffff 0%, var(--checker-white) 52%, #c0b288 100%); - border: 1.8px solid var(--checker-ring); - box-shadow: - 0 2px 6px rgba(0,0,0,0.4), - inset 0 -1px 3px rgba(0,0,0,0.15); - color: #443322; -} - -.checker.black { - background-image: - radial-gradient(ellipse 40% 28% at 36% 30%, - rgba(110,65,30,0.38) 0%, transparent 100%), - radial-gradient(circle, - transparent 68%, rgba(200,164,72,0.18) 68.5%, - rgba(200,164,72,0.18) 71.5%, transparent 72%), - radial-gradient(circle, - transparent 43%, rgba(200,164,72,0.13) 43.5%, - rgba(200,164,72,0.13) 46.5%, transparent 47%), - radial-gradient(circle at 38% 32%, - #4a2e1a 0%, #1c1008 45%, var(--checker-black) 100%); - border: 1.8px solid var(--checker-ring); - box-shadow: - 0 2px 6px rgba(0,0,0,0.55), - inset 0 -1px 3px rgba(0,0,0,0.4); - color: #c8b898; -} - -/* ── Hole toast (§6a) ───────────────────────────────────────────────── */ -@keyframes toast-rise { - from { transform: translate(-50%, -40%); opacity: 0; } - to { transform: translate(-50%, -50%); opacity: 1; } -} -@keyframes toast-fade { - from { opacity: 1; } - to { opacity: 0; pointer-events: none; } -} - -.hole-toast { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: rgba(22,10,2,0.93); - border: 2px solid var(--ui-gold); - border-radius: 8px; - padding: 1.5rem 3.5rem; - text-align: center; - z-index: 50; - pointer-events: none; - box-shadow: - 0 12px 40px rgba(0,0,0,0.65), - 0 0 0 1px rgba(200,164,72,0.25), - inset 0 1px 0 rgba(200,164,72,0.1); - animation: - toast-rise 0.25s cubic-bezier(0.22, 0.61, 0.36, 1), - toast-fade 0.5s ease-in 1.4s forwards; -} - -.hole-toast-title { - font-family: var(--font-display); - font-size: 3.25rem; - font-weight: 600; - color: var(--ui-gold); - letter-spacing: 0.1em; - line-height: 1; -} - -.hole-toast-count { - font-family: var(--font-display); - font-size: 1.1rem; - color: rgba(200,164,72,0.68); - margin-top: 0.35rem; - letter-spacing: 0.06em; -} - -.hole-toast-bredouille { - font-family: var(--font-ui); - font-size: 0.75rem; - letter-spacing: 0.08em; - color: rgba(200,164,72,0.55); - margin-top: 0.4rem; - text-transform: uppercase; -} - -/* ── Bredouille toast variant (§6d) — gold shimmer, larger entrance ─── */ -@keyframes bredouille-shimmer { - 0%, 100% { box-shadow: 0 12px 40px rgba(0,0,0,0.65), 0 0 0 2px rgba(200,164,72,0.4), inset 0 0 0 rgba(200,164,72,0); } - 50% { box-shadow: 0 12px 40px rgba(0,0,0,0.65), 0 0 0 4px rgba(200,164,72,0.7), inset 0 0 24px rgba(200,164,72,0.08); } -} -.hole-toast.hole-toast-bredouille { - border-width: 2.5px; - border-color: var(--ui-gold); - padding: 2rem 4rem; - animation: - toast-rise 0.3s cubic-bezier(0.22, 0.61, 0.36, 1), - bredouille-shimmer 0.9s ease-in-out 0.3s 2, - toast-fade 0.5s ease-in 2.2s forwards; -} -.hole-toast.hole-toast-bredouille .hole-toast-title { - font-size: 3.75rem; -} -.hole-toast.hole-toast-bredouille .hole-toast-bredouille { - font-size: 0.85rem; - color: rgba(200,164,72,0.8); - letter-spacing: 0.14em; -} - -/* ── §4a — Checker slide animation ─────────────────────────────────── */ -@keyframes checker-slide-in { - from { transform: translate(var(--slide-dx, 0px), var(--slide-dy, 0px)); } - to { transform: none; } -} -/* Only the arriving (outermost) checker animates; --slide-dx/dy are set - as inline styles on that element at render time, so no flash occurs. */ -.checker.arriving { - animation: checker-slide-in 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94); -} -/* Lift the field that owns an arriving checker above its siblings so the - checker doesn't slide under adjacent fields (isolation:isolate traps - z-index within each field's stacking context). */ -.field:has(.checker.arriving) { - isolation: auto; - z-index: 10; - position: relative; -} - -/* ── Checker lift on selected field (§4b) ───────────────────────────── */ -.field.selected .checker-stack { - transform: translateY(-5px); - filter: drop-shadow(0 8px 12px rgba(0,0,0,0.6)); - transition: transform 0.12s ease-out, filter 0.12s ease-out; -} - -/* ── Action buttons below board (§10c) ──────────────────────────────── */ -.board-actions { - display: flex; - gap: 0.55rem; - justify-content: center; - align-items: center; - flex-wrap: wrap; - min-height: 2rem; /* reserve height so layout doesn't shift when buttons appear */ -} diff --git a/clients/web-game/src/app.rs b/clients/web-game/src/app.rs index a518fcb..ce355f4 100644 --- a/clients/web-game/src/app.rs +++ b/clients/web-game/src/app.rs @@ -19,7 +19,7 @@ use trictrac_store::CheckerMove; use std::collections::VecDeque; -const RELAY_URL: &str = "ws://127.0.0.1:8080/ws"; +const RELAY_URL: &str = "ws://localhost:8080/ws"; const GAME_ID: &str = "trictrac"; const STORAGE_KEY: &str = "trictrac_session"; @@ -152,6 +152,23 @@ pub fn App() -> impl IntoView { }; let screen = RwSignal::new(initial_screen); + // Auth: fetch once and expose to all child components via context. + let auth_username: RwSignal> = RwSignal::new(None); + provide_context(auth_username); + spawn_local(async move { + if let Ok(resp) = gloo_net::http::Request::get(&format!("{HTTP_BASE}/auth/me")) + .credentials(web_sys::RequestCredentials::Include) + .send() + .await + { + if resp.status() == 200 { + if let Ok(me) = resp.json::().await { + auth_username.set(Some(me.username)); + } + } + } + }); + let (cmd_tx, mut cmd_rx) = mpsc::unbounded::(); let pending: RwSignal> = RwSignal::new(VecDeque::new()); provide_context(pending); @@ -275,6 +292,7 @@ pub fn App() -> impl IntoView { let player_id = session.player_id; let reconnect_token = session.reconnect_token; let mut vs = ViewState::default_with_names("Blancs", "Noirs"); + let mut result_submitted = false; loop { futures::select! { @@ -297,6 +315,15 @@ pub fn App() -> impl IntoView { ViewStateUpdate::Full(state) => vs = state, ViewStateUpdate::Incremental(delta) => vs.apply_delta(&delta), } + + // Host reports outcomes once per terminal game state. + if is_host && !result_submitted && vs.stage == SerStage::Ended { + result_submitted = true; + let room = room_id_for_storage.clone(); + let gs = vs.clone(); + spawn_local(submit_game_result(room, gs)); + } + if is_host { save_session(&StoredSession { relay_url: RELAY_URL.to_string(), diff --git a/clients/web-game/src/components/game_screen.rs b/clients/web-game/src/components/game_screen.rs index 909e266..2493680 100644 --- a/clients/web-game/src/components/game_screen.rs +++ b/clients/web-game/src/components/game_screen.rs @@ -18,6 +18,8 @@ use super::scoring::ScoringPanel; pub fn GameScreen(state: GameUiState) -> impl IntoView { let i18n = use_i18n(); + let auth_username = + use_context::>>().expect("auth_username not found in context"); let vs = state.view_state.clone(); let player_id = state.player_id; let is_my_turn = vs.active_mp_player == Some(player_id); @@ -240,6 +242,11 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView { on:click=move |_| i18n.set_locale(Locale::fr) >"FR" + + {move || auth_username.get().map(|u| view! { +

"Playing as " {u}

+ })} + ) -> impl IntoView { let i18n = use_i18n(); @@ -11,6 +16,8 @@ pub fn LoginScreen(error: Option) -> impl IntoView { let cmd_tx = use_context::>() .expect("UnboundedSender not found in context"); + let auth_username = + use_context::>>().expect("auth_username not found in context"); let cmd_tx_create = cmd_tx.clone(); let cmd_tx_join = cmd_tx.clone(); @@ -47,6 +54,19 @@ pub fn LoginScreen(error: Option) -> impl IntoView { {error.map(|err| view! {

{err}

})} + // Auth status badge + {move || match auth_username.get() { + Some(u) => view! { +

"✓ Logged in as " {u}

+ }.into_any(), + None => view! { +

+ "Not logged in — games won't be tracked. " + "Create account" +

+ }.into_any(), + }} +