p2p-lockstep-kit-ui 0.1.2 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region \0rolldown/runtime.js
2
- var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), s = (e, i, o, s) => {
2
+ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), s = (e, i, o, s) => {
3
3
  if (i && typeof i == "object" || typeof i == "function") for (var c = r(i), l = 0, u = c.length, d; l < u; l++) d = c[l], !a.call(e, d) && d !== o && t(e, d, {
4
4
  get: ((e) => i[e]).bind(null, d),
5
5
  enumerable: !(s = n(i, d)) || s.enumerable
@@ -21,6 +21,10 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
21
21
  canStart: !1,
22
22
  canUndo: !1,
23
23
  canRestart: !1,
24
+ canOfferDraw: !1,
25
+ canResign: !1,
26
+ allowDraw: !1,
27
+ allowResign: !1,
24
28
  started: !1,
25
29
  connectionState: "idle"
26
30
  }, d = class extends HTMLElement {
@@ -39,22 +43,22 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
39
43
  return this.#e;
40
44
  }
41
45
  render() {
42
- let { canReady: e, canStart: t, canUndo: n, canRestart: r, connected: i, readySelf: a, started: o } = this.#e, s = t ? "start" : a || e ? "ready" : "", c = t ? "Start" : a ? "Unready" : e ? "Ready" : i ? "Waiting" : "Waiting for connection", l = !o || !!s, u = o ? "In game" : t ? "Start ready" : a ? "Ready" : "Setup";
46
+ let { canReady: e, canStart: t, canUndo: n, canRestart: r, canOfferDraw: i, canResign: a, allowDraw: o, allowResign: s, connected: c, readySelf: l, started: u } = this.#e, d = t ? "start" : l || e ? "ready" : "", f = t ? "Start" : l ? "Unready" : e ? "Ready" : c ? "Waiting" : "Waiting for connection", p = !u || !!d, m = u ? "In game" : t ? "Start ready" : l ? "Ready" : "Setup";
43
47
  this.className = "block", this.innerHTML = `
44
48
  <section class="lock-panel rounded-[1.25rem] p-2.5 sm:rounded-[1.5rem] sm:p-3 lg:rounded-[1.75rem] lg:p-2.5">
45
49
  <div class="flex flex-col gap-2 sm:gap-3">
46
50
  <div class="hidden items-center justify-between gap-3 px-1 lg:flex">
47
51
  <p class="text-[0.62rem] font-semibold uppercase tracking-[0.2em] text-[var(--lock-dim)]">Controls</p>
48
- <span class="rounded-full border border-[var(--lock-border)] px-2.5 py-1 text-[0.65rem] text-[var(--lock-muted)]">${u}</span>
52
+ <span class="rounded-full border border-[var(--lock-border)] px-2.5 py-1 text-[0.65rem] text-[var(--lock-muted)]">${m}</span>
49
53
  </div>
50
54
 
51
- ${l ? `<button
55
+ ${p ? `<button
52
56
  type="button"
53
- data-action="${s}"
57
+ data-action="${d}"
54
58
  class="lock-disabled inline-flex items-center justify-center rounded-full px-4 py-2.5 text-sm font-semibold transition sm:py-3 ${t ? "lock-primary" : e ? "bg-[var(--lock-teal)] text-[#08120f] shadow-[0_12px_32px_rgba(110,231,200,0.22)] hover:brightness-105" : "lock-secondary"}"
55
- ${s ? "" : "disabled"}
59
+ ${d ? "" : "disabled"}
56
60
  >
57
- ${c}
61
+ ${f}
58
62
  </button>` : ""}
59
63
 
60
64
  <div class="grid grid-cols-2 gap-2 sm:gap-3">
@@ -74,6 +78,22 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
74
78
  >
75
79
  Restart
76
80
  </button>
81
+ ${o ? `<button
82
+ type="button"
83
+ data-action="draw"
84
+ class="lock-secondary lock-disabled inline-flex items-center justify-center rounded-full px-3 py-2.5 text-xs font-semibold transition sm:px-4 sm:py-3 sm:text-sm"
85
+ ${i ? "" : "disabled"}
86
+ >
87
+ Offer draw
88
+ </button>` : ""}
89
+ ${s ? `<button
90
+ type="button"
91
+ data-action="resign"
92
+ class="lock-disabled inline-flex items-center justify-center rounded-full border border-[var(--lock-border)] bg-[var(--lock-error-bg)] px-3 py-2.5 text-xs font-semibold text-[var(--lock-rose)] transition hover:border-[var(--lock-border-strong)] sm:px-4 sm:py-3 sm:text-sm"
93
+ ${a ? "" : "disabled"}
94
+ >
95
+ Resign
96
+ </button>` : ""}
77
97
  </div>
78
98
  </div>
79
99
  </section>
@@ -349,7 +369,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
349
369
  }
350
370
  return i;
351
371
  };
352
- })), te = /* @__PURE__ */ o(((e) => {
372
+ })), w = /* @__PURE__ */ o(((e) => {
353
373
  var t = y(), n = [
354
374
  1,
355
375
  1,
@@ -690,8 +710,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
690
710
  default: return;
691
711
  }
692
712
  };
693
- })), w = /* @__PURE__ */ o(((e) => {
694
- var t = new Uint8Array(512), n = new Uint8Array(256);
713
+ })), T = /* @__PURE__ */ o(((e) => {
714
+ var t = /* @__PURE__ */ new Uint8Array(512), n = /* @__PURE__ */ new Uint8Array(256);
695
715
  (function() {
696
716
  let e = 1;
697
717
  for (let r = 0; r < 255; r++) t[r] = e, n[e] = r, e <<= 1, e & 256 && (e ^= 285);
@@ -704,8 +724,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
704
724
  }, e.mul = function(e, r) {
705
725
  return e === 0 || r === 0 ? 0 : t[n[e] + n[r]];
706
726
  };
707
- })), ne = /* @__PURE__ */ o(((e) => {
708
- var t = w();
727
+ })), E = /* @__PURE__ */ o(((e) => {
728
+ var t = T();
709
729
  e.mul = function(e, n) {
710
730
  let r = new Uint8Array(e.length + n.length - 1);
711
731
  for (let i = 0; i < e.length; i++) for (let a = 0; a < n.length; a++) r[i + a] ^= t.mul(e[i], n[a]);
@@ -725,8 +745,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
725
745
  for (let i = 0; i < n; i++) r = e.mul(r, new Uint8Array([1, t.exp(i)]));
726
746
  return r;
727
747
  };
728
- })), re = /* @__PURE__ */ o(((e, t) => {
729
- var n = ne();
748
+ })), te = /* @__PURE__ */ o(((e, t) => {
749
+ var n = E();
730
750
  function r(e) {
731
751
  this.genPoly = void 0, this.degree = e, this.degree && this.initialize(this.degree);
732
752
  }
@@ -743,16 +763,16 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
743
763
  }
744
764
  return r;
745
765
  }, t.exports = r;
746
- })), T = /* @__PURE__ */ o(((e) => {
766
+ })), D = /* @__PURE__ */ o(((e) => {
747
767
  e.isValid = function(e) {
748
768
  return !isNaN(e) && e >= 1 && e <= 40;
749
769
  };
750
- })), E = /* @__PURE__ */ o(((e) => {
770
+ })), O = /* @__PURE__ */ o(((e) => {
751
771
  var t = "[0-9]+", n = "[A-Z $%*+\\-./:]+", r = "(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+";
752
772
  r = r.replace(/u/g, "\\u");
753
773
  var i = "(?:(?![A-Z0-9 $%*+\\-./:]|" + r + ")(?:.|[\r\n]))+";
754
774
  e.KANJI = new RegExp(r, "g"), e.BYTE_KANJI = /* @__PURE__ */ RegExp("[^A-Z0-9 $%*+\\-./:]+", "g"), e.BYTE = new RegExp(i, "g"), e.NUMERIC = new RegExp(t, "g"), e.ALPHANUMERIC = new RegExp(n, "g");
755
- var a = RegExp("^" + r + "$"), o = RegExp("^" + t + "$"), s = /* @__PURE__ */ RegExp("^[A-Z0-9 $%*+\\-./:]+$");
775
+ var a = RegExp("^" + r + "$"), o = /* @__PURE__ */ RegExp("^[0-9]+$"), s = /* @__PURE__ */ RegExp("^[A-Z0-9 $%*+\\-./:]+$");
756
776
  e.testKanji = function(e) {
757
777
  return a.test(e);
758
778
  }, e.testNumeric = function(e) {
@@ -760,8 +780,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
760
780
  }, e.testAlphanumeric = function(e) {
761
781
  return s.test(e);
762
782
  };
763
- })), D = /* @__PURE__ */ o(((e) => {
764
- var t = T(), n = E();
783
+ })), k = /* @__PURE__ */ o(((e) => {
784
+ var t = D(), n = O();
765
785
  e.NUMERIC = {
766
786
  id: "Numeric",
767
787
  bit: 1,
@@ -824,8 +844,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
824
844
  return n;
825
845
  }
826
846
  };
827
- })), ie = /* @__PURE__ */ o(((e) => {
828
- var t = v(), n = te(), r = y(), i = D(), a = T(), o = 7973, s = t.getBCHDigit(o);
847
+ })), ne = /* @__PURE__ */ o(((e) => {
848
+ var t = v(), n = w(), r = y(), i = k(), a = D(), o = 7973, s = t.getBCHDigit(o);
829
849
  function c(t, n, r) {
830
850
  for (let i = 1; i <= 40; i++) if (n <= e.getCapacity(i, r, t)) return i;
831
851
  }
@@ -871,15 +891,15 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
871
891
  for (; t.getBCHDigit(n) - s >= 0;) n ^= o << t.getBCHDigit(n) - s;
872
892
  return e << 12 | n;
873
893
  };
874
- })), ae = /* @__PURE__ */ o(((e) => {
894
+ })), re = /* @__PURE__ */ o(((e) => {
875
895
  var t = v(), n = 1335, r = 21522, i = t.getBCHDigit(n);
876
896
  e.getEncodedBits = function(e, a) {
877
897
  let o = e.bit << 3 | a, s = o << 10;
878
898
  for (; t.getBCHDigit(s) - i >= 0;) s ^= n << t.getBCHDigit(s) - i;
879
899
  return (o << 10 | s) ^ r;
880
900
  };
881
- })), oe = /* @__PURE__ */ o(((e, t) => {
882
- var n = D();
901
+ })), A = /* @__PURE__ */ o(((e, t) => {
902
+ var n = k();
883
903
  function r(e) {
884
904
  this.mode = n.NUMERIC, this.data = e.toString();
885
905
  }
@@ -895,8 +915,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
895
915
  let i = this.data.length - t;
896
916
  i > 0 && (n = this.data.substr(t), r = parseInt(n, 10), e.put(r, i * 3 + 1));
897
917
  }, t.exports = r;
898
- })), se = /* @__PURE__ */ o(((e, t) => {
899
- var n = D(), r = /* @__PURE__ */ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");
918
+ })), ie = /* @__PURE__ */ o(((e, t) => {
919
+ var n = k(), r = /* @__PURE__ */ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");
900
920
  function i(e) {
901
921
  this.mode = n.ALPHANUMERIC, this.data = e;
902
922
  }
@@ -914,8 +934,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
914
934
  }
915
935
  this.data.length % 2 && e.put(r.indexOf(this.data[t]), 6);
916
936
  }, t.exports = i;
917
- })), ce = /* @__PURE__ */ o(((e, t) => {
918
- var n = D();
937
+ })), ae = /* @__PURE__ */ o(((e, t) => {
938
+ var n = k();
919
939
  function r(e) {
920
940
  this.mode = n.BYTE, typeof e == "string" ? this.data = new TextEncoder().encode(e) : this.data = new Uint8Array(e);
921
941
  }
@@ -928,8 +948,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
928
948
  }, r.prototype.write = function(e) {
929
949
  for (let t = 0, n = this.data.length; t < n; t++) e.put(this.data[t], 8);
930
950
  }, t.exports = r;
931
- })), le = /* @__PURE__ */ o(((e, t) => {
932
- var n = D(), r = v();
951
+ })), oe = /* @__PURE__ */ o(((e, t) => {
952
+ var n = k(), r = v();
933
953
  function i(e) {
934
954
  this.mode = n.KANJI, this.data = e;
935
955
  }
@@ -949,7 +969,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
949
969
  n = (n >>> 8 & 255) * 192 + (n & 255), e.put(n, 13);
950
970
  }
951
971
  }, t.exports = i;
952
- })), ue = /* @__PURE__ */ o(((e, t) => {
972
+ })), se = /* @__PURE__ */ o(((e, t) => {
953
973
  var n = {
954
974
  single_source_shortest_paths: function(e, t, r) {
955
975
  var i = {}, a = {};
@@ -1002,8 +1022,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1002
1022
  }
1003
1023
  };
1004
1024
  t !== void 0 && (t.exports = n);
1005
- })), de = /* @__PURE__ */ o(((e) => {
1006
- var t = D(), n = oe(), r = se(), i = ce(), a = le(), o = E(), s = v(), c = ue();
1025
+ })), ce = /* @__PURE__ */ o(((e) => {
1026
+ var t = k(), n = A(), r = ie(), i = ae(), a = oe(), o = O(), s = v(), c = se();
1007
1027
  function l(e) {
1008
1028
  return unescape(encodeURIComponent(e)).length;
1009
1029
  }
@@ -1130,8 +1150,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1130
1150
  }, e.rawSplit = function(t) {
1131
1151
  return e.fromArray(d(t, s.isKanjiModeEnabled()));
1132
1152
  };
1133
- })), fe = /* @__PURE__ */ o(((e) => {
1134
- var t = v(), n = y(), r = b(), i = x(), a = S(), o = C(), s = ee(), c = te(), l = re(), u = ie(), d = ae(), f = D(), p = de();
1153
+ })), le = /* @__PURE__ */ o(((e) => {
1154
+ var t = v(), n = y(), r = b(), i = x(), a = S(), o = C(), s = ee(), c = w(), l = te(), u = ne(), d = re(), f = k(), p = ce();
1135
1155
  function m(e, t) {
1136
1156
  let n = e.size, r = o.getPositions(t);
1137
1157
  for (let t = 0; t < r.length; t++) {
@@ -1157,12 +1177,12 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1157
1177
  let n = e.size, r = u.getEncodedBits(t), i, a, o;
1158
1178
  for (let t = 0; t < 18; t++) i = Math.floor(t / 3), a = t % 3 + n - 8 - 3, o = (r >> t & 1) == 1, e.set(i, a, o, !0), e.set(a, i, o, !0);
1159
1179
  }
1160
- function w(e, t, n) {
1180
+ function T(e, t, n) {
1161
1181
  let r = e.size, i = d.getEncodedBits(t, n), a, o;
1162
1182
  for (a = 0; a < 15; a++) o = (i >> a & 1) == 1, a < 6 ? e.set(a, 8, o, !0) : a < 8 ? e.set(a + 1, 8, o, !0) : e.set(r - 15 + a, 8, o, !0), a < 8 ? e.set(8, r - a - 1, o, !0) : a < 9 ? e.set(8, 15 - a - 1 + 1, o, !0) : e.set(8, 15 - a - 1, o, !0);
1163
1183
  e.set(r - 8, 8, 1, !0);
1164
1184
  }
1165
- function ne(e, t) {
1185
+ function E(e, t) {
1166
1186
  let n = e.size, r = -1, i = n - 1, a = 7, o = 0;
1167
1187
  for (let s = n - 1; s > 0; s -= 2) for (s === 6 && s--;;) {
1168
1188
  for (let n = 0; n < 2; n++) if (!e.isReserved(i, s - n)) {
@@ -1175,7 +1195,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1175
1195
  }
1176
1196
  }
1177
1197
  }
1178
- function T(e, n, i) {
1198
+ function D(e, n, i) {
1179
1199
  let a = new r();
1180
1200
  i.forEach(function(t) {
1181
1201
  a.put(t.mode.bit, 4), a.put(t.getLength(), f.getCharCountIndicator(t.mode, e)), t.write(a);
@@ -1184,9 +1204,9 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1184
1204
  for (a.getLengthInBits() + 4 <= o && a.put(0, 4); a.getLengthInBits() % 8 != 0;) a.putBit(0);
1185
1205
  let s = (o - a.getLengthInBits()) / 8;
1186
1206
  for (let e = 0; e < s; e++) a.put(e % 2 ? 17 : 236, 8);
1187
- return E(a, e, n);
1207
+ return O(a, e, n);
1188
1208
  }
1189
- function E(e, n, r) {
1209
+ function O(e, n, r) {
1190
1210
  let i = t.getSymbolTotalCodewords(n), a = i - c.getTotalCodewordsCount(n, r), o = c.getBlocksCount(n, r), s = o - i % o, u = Math.floor(i / o), d = Math.floor(a / o), f = d + 1, p = u - d, m = new l(p), h = 0, g = Array(o), _ = Array(o), v = 0, y = new Uint8Array(e.buffer);
1191
1211
  for (let e = 0; e < o; e++) {
1192
1212
  let t = e < s ? d : f;
@@ -1197,7 +1217,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1197
1217
  for (S = 0; S < p; S++) for (C = 0; C < o; C++) b[x++] = _[C][S];
1198
1218
  return b;
1199
1219
  }
1200
- function oe(e, n, r, a) {
1220
+ function A(e, n, r, a) {
1201
1221
  let o;
1202
1222
  if (Array.isArray(e)) o = p.fromArray(e);
1203
1223
  else if (typeof e == "string") {
@@ -1212,8 +1232,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1212
1232
  if (!c) throw Error("The amount of data is too big to be stored in a QR Code");
1213
1233
  if (!n) n = c;
1214
1234
  else if (n < c) throw Error("\nThe chosen QR Code version cannot contain this amount of data.\nMinimum version required to store current data is: " + c + ".\n");
1215
- let l = T(n, r, o), d = new i(t.getSymbolSize(n));
1216
- return m(d, n), h(d), g(d, n), w(d, r, 0), n >= 7 && _(d, n), ne(d, l), isNaN(a) && (a = s.getBestMask(d, w.bind(null, d, r))), s.applyMask(a, d), w(d, r, a), {
1235
+ let l = D(n, r, o), d = new i(t.getSymbolSize(n));
1236
+ return m(d, n), h(d), g(d, n), T(d, r, 0), n >= 7 && _(d, n), E(d, l), isNaN(a) && (a = s.getBestMask(d, T.bind(null, d, r))), s.applyMask(a, d), T(d, r, a), {
1217
1237
  modules: d,
1218
1238
  version: n,
1219
1239
  errorCorrectionLevel: r,
@@ -1224,9 +1244,9 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1224
1244
  e.create = function(e, r) {
1225
1245
  if (e === void 0 || e === "") throw Error("No input text");
1226
1246
  let i = n.M, a, o;
1227
- return r !== void 0 && (i = n.from(r.errorCorrectionLevel, n.M), a = u.from(r.version), o = s.from(r.maskPattern), r.toSJISFunc && t.setToSJISFunction(r.toSJISFunc)), oe(e, a, i, o);
1247
+ return r !== void 0 && (i = n.from(r.errorCorrectionLevel, n.M), a = u.from(r.version), o = s.from(r.maskPattern), r.toSJISFunc && t.setToSJISFunction(r.toSJISFunc)), A(e, a, i, o);
1228
1248
  };
1229
- })), pe = /* @__PURE__ */ o(((e) => {
1249
+ })), j = /* @__PURE__ */ o(((e) => {
1230
1250
  function t(e) {
1231
1251
  if (typeof e == "number" && (e = e.toString()), typeof e != "string") throw Error("Color should be defined as hex string");
1232
1252
  let t = e.slice().replace("#", "").split("");
@@ -1273,8 +1293,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1273
1293
  t[u++] = d.r, t[u++] = d.g, t[u++] = d.b, t[u] = d.a;
1274
1294
  }
1275
1295
  };
1276
- })), me = /* @__PURE__ */ o(((e) => {
1277
- var t = pe();
1296
+ })), ue = /* @__PURE__ */ o(((e) => {
1297
+ var t = j();
1278
1298
  function n(e, t, n) {
1279
1299
  e.clearRect(0, 0, t.width, t.height), t.style ||= {}, t.height = n, t.width = n, t.style.height = n + "px", t.style.width = n + "px";
1280
1300
  }
@@ -1296,8 +1316,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1296
1316
  let a = e.render(t, n, i), o = i.type || "image/png", s = i.rendererOpts || {};
1297
1317
  return a.toDataURL(o, s.quality);
1298
1318
  };
1299
- })), he = /* @__PURE__ */ o(((e) => {
1300
- var t = pe();
1319
+ })), de = /* @__PURE__ */ o(((e) => {
1320
+ var t = j();
1301
1321
  function n(e, t) {
1302
1322
  let n = e.a / 255, r = t + "=\"" + e.hex + "\"";
1303
1323
  return n < 1 ? r + " " + t + "-opacity=\"" + n.toFixed(2).slice(1) + "\"" : r;
@@ -1318,8 +1338,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1318
1338
  let o = t.getOptions(r), s = e.modules.size, c = e.modules.data, l = s + o.margin * 2, u = o.color.light.a ? "<path " + n(o.color.light, "fill") + " d=\"M0 0h" + l + "v" + l + "H0z\"/>" : "", d = "<path " + n(o.color.dark, "stroke") + " d=\"" + i(c, s, o.margin) + "\"/>", f = "viewBox=\"0 0 " + l + " " + l + "\"", p = "<svg xmlns=\"http://www.w3.org/2000/svg\" " + (o.width ? "width=\"" + o.width + "\" height=\"" + o.width + "\" " : "") + f + " shape-rendering=\"crispEdges\">" + u + d + "</svg>\n";
1319
1339
  return typeof a == "function" && a(null, p), p;
1320
1340
  };
1321
- })), ge = /* @__PURE__ */ c((/* @__PURE__ */ o(((e) => {
1322
- var t = _(), n = fe(), r = me(), i = he();
1341
+ })), fe = /* @__PURE__ */ c((/* @__PURE__ */ o(((e) => {
1342
+ var t = _(), n = le(), r = ue(), i = de();
1323
1343
  function a(e, r, i, a, o) {
1324
1344
  let s = [].slice.call(arguments, 1), c = s.length, l = typeof s[c - 1] == "function";
1325
1345
  if (!l && !t()) throw Error("Callback required as last argument");
@@ -1346,12 +1366,12 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1346
1366
  e.create = n.create, e.toCanvas = a.bind(null, r.render), e.toDataURL = a.bind(null, r.renderToDataURL), e.toString = a.bind(null, function(e, t, n) {
1347
1367
  return i.render(e, n);
1348
1368
  });
1349
- })))(), 1), _e = {
1369
+ })))(), 1), pe = {
1350
1370
  peerId: "",
1351
1371
  signalUrl: "",
1352
1372
  shareUrl: ""
1353
- }, ve = class extends HTMLElement {
1354
- #e = _e;
1373
+ }, M = class extends HTMLElement {
1374
+ #e = pe;
1355
1375
  #t = !1;
1356
1376
  connectedCallback() {
1357
1377
  this.#t || (this.#t = !0, this.addEventListener("click", this.#n), this.render());
@@ -1398,7 +1418,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1398
1418
  return;
1399
1419
  }
1400
1420
  try {
1401
- await ge.toCanvas(t, e, {
1421
+ await fe.toCanvas(t, e, {
1402
1422
  width: 208,
1403
1423
  margin: 1,
1404
1424
  color: {
@@ -1413,7 +1433,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1413
1433
  #n = (e) => {
1414
1434
  e.target?.closest("[data-action='copy-share']") && l(this, "lockstep-copy-share", { value: this.#e.shareUrl });
1415
1435
  };
1416
- }, ye = {
1436
+ }, me = {
1417
1437
  theme: "light",
1418
1438
  gameTitle: "P2P Lockstep",
1419
1439
  peerId: "",
@@ -1427,14 +1447,20 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1427
1447
  readySelf: !1,
1428
1448
  readyPeer: !1,
1429
1449
  pendingAction: null,
1450
+ outcome: null,
1430
1451
  sessionId: "default-session",
1431
1452
  historyLength: 0,
1432
1453
  lastStart: null,
1433
1454
  lastError: ""
1434
- }, O = (e) => e === "me" ? "Your turn" : e === "peer" ? "Peer turn" : "Waiting", k = (e, t) => t === "ready" ? `${e} ready` : t === "could_start" ? e === "Me" ? "You can start" : "Peer can start" : `${e} idle`, A = (e, t) => {
1455
+ }, N = (e) => e === "me" ? "Your turn" : e === "peer" ? "Peer turn" : "Waiting", he = (e) => {
1456
+ if (!e) return null;
1457
+ if (e.kind === "draw") return e.reason === "agreement" ? "Draw by agreement" : "Draw by mutual resignation";
1458
+ let t = e.winner === "local" ? "You win" : "Peer wins";
1459
+ return e.reason === "resignation" ? `${t} by resignation` : t;
1460
+ }, P = (e, t) => t === "ready" ? `${e} ready` : t === "could_start" ? e === "Me" ? "You can start" : "Peer can start" : `${e} idle`, ge = (e, t) => {
1435
1461
  let n = e === "Local" ? "You" : "Peer";
1436
1462
  return t === "idle" ? `${n} idle` : t === "ready" ? `${n} ready` : t === "could_start" ? `${n} can start` : `${n} ${t.replaceAll("_", " ")}`;
1437
- }, be = (e, t) => e || t === "connected" ? {
1463
+ }, _e = (e, t) => e || t === "connected" ? {
1438
1464
  label: "Live",
1439
1465
  detail: "connected",
1440
1466
  tone: "bg-[var(--lock-teal)]"
@@ -1462,8 +1488,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1462
1488
  label: "Standby",
1463
1489
  detail: "not registered",
1464
1490
  tone: "bg-[var(--lock-dim)]"
1465
- }, j = class extends HTMLElement {
1466
- #e = ye;
1491
+ }, ve = class extends HTMLElement {
1492
+ #e = me;
1467
1493
  #t = !1;
1468
1494
  connectedCallback() {
1469
1495
  this.#t || (this.#t = !0, this.addEventListener("click", this.#n), this.render());
@@ -1478,14 +1504,14 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1478
1504
  return this.#e;
1479
1505
  }
1480
1506
  render() {
1481
- let { connected: e, pendingAction: t } = this.#e, n = be(e, this.#e.connectionState), r = k("Me", this.#e.localState), i = k("Peer", this.#e.remoteState), a = A("Local", this.#e.localState), o = A("Remote", this.#e.remoteState), s = `${r} / ${i}`, c = this.#e.lastStart === "local" ? "You" : this.#e.lastStart === "remote" ? "Peer" : "Not started", l = `${this.#e.historyLength} move${this.#e.historyLength === 1 ? "" : "s"} / ${c}`;
1507
+ let { connected: e, pendingAction: t } = this.#e, n = he(this.#e.outcome), r = _e(e, this.#e.connectionState), i = P("Me", this.#e.localState), a = P("Peer", this.#e.remoteState), o = ge("Local", this.#e.localState), s = ge("Remote", this.#e.remoteState), c = `${i} / ${a}`, l = this.#e.lastStart === "local" ? "You" : this.#e.lastStart === "remote" ? "Peer" : "Not started", u = n ?? `${this.#e.historyLength} move${this.#e.historyLength === 1 ? "" : "s"} / ${l}`;
1482
1508
  this.className = "block", this.innerHTML = `
1483
1509
  <section class="lock-panel relative rounded-[1.25rem] p-2.5 text-sm text-[var(--lock-muted)] sm:rounded-[1.5rem] sm:p-3 lg:rounded-[1.75rem] lg:p-2.5">
1484
1510
  <div class="flex items-center gap-2 lg:hidden">
1485
1511
  <div class="min-w-0 flex-1">
1486
1512
  <p data-mobile-title class="truncate text-sm font-semibold leading-tight text-[var(--lock-paper)]"></p>
1487
1513
  <div class="mt-1 flex min-w-0 items-center gap-1.5 text-[0.68rem] leading-none text-[var(--lock-muted)]">
1488
- <span class="h-2 w-2 shrink-0 rounded-full ${n.tone}"></span>
1514
+ <span class="h-2 w-2 shrink-0 rounded-full ${r.tone}"></span>
1489
1515
  <span data-mobile-connection class="shrink-0 font-medium"></span>
1490
1516
  <span class="text-[var(--lock-dim)]">/</span>
1491
1517
  <span data-mobile-turn class="min-w-0 truncate"></span>
@@ -1542,6 +1568,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1542
1568
  <span data-detail-local-state class="rounded-full border border-[var(--lock-border)] px-2.5 py-1 text-[0.68rem] text-[var(--lock-muted)]"></span>
1543
1569
  <span data-detail-remote-state class="rounded-full border border-[var(--lock-border)] px-2.5 py-1 text-[0.68rem] text-[var(--lock-muted)]"></span>
1544
1570
  ${t ? `<span class="rounded-full border border-[var(--lock-border-strong)] bg-[var(--lock-pending-bg)] px-2.5 py-1 text-[0.68rem] text-[var(--lock-bronze-bright)]">Pending ${t}</span>` : ""}
1571
+ ${n ? `<span class="rounded-full border border-[var(--lock-border-strong)] bg-[var(--lock-pending-bg)] px-2.5 py-1 text-[0.68rem] text-[var(--lock-bronze-bright)]">${n}</span>` : ""}
1545
1572
  </div>
1546
1573
  </div>
1547
1574
  </details>
@@ -1556,7 +1583,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1556
1583
  <p data-match-meta class="mt-1.5 text-xs text-[var(--lock-muted)]"></p>
1557
1584
  </div>
1558
1585
  <div class="flex shrink-0 items-center gap-2">
1559
- <span class="h-2.5 w-2.5 rounded-full ${n.tone}"></span>
1586
+ <span class="h-2.5 w-2.5 rounded-full ${r.tone}"></span>
1560
1587
  <details class="group relative">
1561
1588
  <summary aria-label="Appearance settings" class="lock-control flex h-7 w-7 cursor-pointer list-none items-center justify-center rounded-full border border-[var(--lock-border)] text-xs font-semibold leading-none text-[var(--lock-muted)] [&::-webkit-details-marker]:hidden">...</summary>
1562
1589
  <div class="lock-surface-strong absolute right-0 z-50 mt-2 w-52 rounded-2xl border border-[var(--lock-border-strong)] p-3 shadow-xl shadow-black/15">
@@ -1574,7 +1601,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1574
1601
  <div class="grid grid-cols-2 gap-2">
1575
1602
  <article class="lock-card rounded-[1rem] border border-[var(--lock-border)] p-2.5">
1576
1603
  <p class="text-[0.58rem] uppercase tracking-[0.2em] text-[var(--lock-dim)]">Connection</p>
1577
- <p class="mt-1.5 text-sm font-semibold text-[var(--lock-paper)]">${n.label}</p>
1604
+ <p class="mt-1.5 text-sm font-semibold text-[var(--lock-paper)]">${r.label}</p>
1578
1605
  <p data-connection-state class="mt-0.5 truncate text-xs text-[var(--lock-muted)]"></p>
1579
1606
  </article>
1580
1607
 
@@ -1613,21 +1640,22 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1613
1640
  <span data-local-state class="rounded-full border border-[var(--lock-border)] px-2 py-0.5 text-[0.64rem] text-[var(--lock-muted)]"></span>
1614
1641
  <span data-remote-state class="rounded-full border border-[var(--lock-border)] px-2 py-0.5 text-[0.64rem] text-[var(--lock-muted)]"></span>
1615
1642
  ${t ? `<span class="rounded-full border border-[var(--lock-border-strong)] bg-[var(--lock-pending-bg)] px-2 py-0.5 text-[0.64rem] text-[var(--lock-bronze-bright)]">Pending ${t}</span>` : ""}
1643
+ ${n ? `<span class="rounded-full border border-[var(--lock-border-strong)] bg-[var(--lock-pending-bg)] px-2 py-0.5 text-[0.64rem] text-[var(--lock-bronze-bright)]">${n}</span>` : ""}
1616
1644
  </div>
1617
1645
  </article>
1618
1646
  </div>
1619
1647
  </section>
1620
- `, p(this, "[data-mobile-title]", this.#e.gameTitle), p(this, "[data-mobile-connection]", n.label), p(this, "[data-mobile-turn]", `#${this.#e.currentTurn} ${O(this.#e.turnOwner)}`), p(this, "[data-mobile-ready]", s), p(this, "[data-detail-connection]", n.label), p(this, "[data-detail-turn]", `#${this.#e.currentTurn} / ${O(this.#e.turnOwner)}`), p(this, "[data-detail-session]", this.#e.sessionId), p(this, "[data-detail-timeline]", l), p(this, "[data-detail-error]", this.#e.lastError), p(this, "[data-detail-peer]", this.#e.peerId || "not set"), p(this, "[data-detail-remote]", this.#e.remotePeerId || "not set"), p(this, "[data-detail-ready-self]", r), p(this, "[data-detail-ready-peer]", i), p(this, "[data-detail-local-state]", a), p(this, "[data-detail-remote-state]", o), p(this, "[data-title]", this.#e.gameTitle), p(this, "[data-match-meta]", l), p(this, "[data-error-message]", this.#e.lastError), p(this, "[data-connection-state]", n.detail), p(this, "[data-current-turn]", `#${this.#e.currentTurn}`), p(this, "[data-turn-owner]", O(this.#e.turnOwner)), p(this, "[data-session-id]", this.#e.sessionId), p(this, "[data-peer-id]", this.#e.peerId || "Local peer ID will appear after register.")?.setAttribute("title", this.#e.peerId), p(this, "[data-remote-peer-id]", this.#e.remotePeerId || "Remote peer not connected yet.")?.setAttribute("title", this.#e.remotePeerId), p(this, "[data-ready-self]", r), p(this, "[data-ready-peer]", i), p(this, "[data-local-state]", a), p(this, "[data-remote-state]", o);
1648
+ `, p(this, "[data-mobile-title]", this.#e.gameTitle), p(this, "[data-mobile-connection]", r.label), p(this, "[data-mobile-turn]", n ?? `#${this.#e.currentTurn} ${N(this.#e.turnOwner)}`), p(this, "[data-mobile-ready]", c), p(this, "[data-detail-connection]", r.label), p(this, "[data-detail-turn]", `#${this.#e.currentTurn} / ${N(this.#e.turnOwner)}`), p(this, "[data-detail-session]", this.#e.sessionId), p(this, "[data-detail-timeline]", u), p(this, "[data-detail-error]", this.#e.lastError), p(this, "[data-detail-peer]", this.#e.peerId || "not set"), p(this, "[data-detail-remote]", this.#e.remotePeerId || "not set"), p(this, "[data-detail-ready-self]", i), p(this, "[data-detail-ready-peer]", a), p(this, "[data-detail-local-state]", o), p(this, "[data-detail-remote-state]", s), p(this, "[data-title]", this.#e.gameTitle), p(this, "[data-match-meta]", u), p(this, "[data-error-message]", this.#e.lastError), p(this, "[data-connection-state]", r.detail), p(this, "[data-current-turn]", n ?? `#${this.#e.currentTurn}`), p(this, "[data-turn-owner]", n ? "Game over" : N(this.#e.turnOwner)), p(this, "[data-session-id]", this.#e.sessionId), p(this, "[data-peer-id]", this.#e.peerId || "Local peer ID will appear after register.")?.setAttribute("title", this.#e.peerId), p(this, "[data-remote-peer-id]", this.#e.remotePeerId || "Remote peer not connected yet.")?.setAttribute("title", this.#e.remotePeerId), p(this, "[data-ready-self]", i), p(this, "[data-ready-peer]", a), p(this, "[data-local-state]", o), p(this, "[data-remote-state]", s);
1621
1649
  }
1622
1650
  #n = (e) => {
1623
1651
  let t = e.target?.closest("button[data-theme-mode]");
1624
1652
  (t?.dataset.themeMode === "light" || t?.dataset.themeMode === "dark") && l(this, "lockstep-theme-change", { theme: t.dataset.themeMode });
1625
1653
  };
1626
- }, xe = {
1654
+ }, ye = {
1627
1655
  open: !1,
1628
1656
  message: ""
1629
- }, M = class extends HTMLElement {
1630
- #e = xe;
1657
+ }, be = class extends HTMLElement {
1658
+ #e = ye;
1631
1659
  #t = !1;
1632
1660
  connectedCallback() {
1633
1661
  this.#t || (this.#t = !0, this.render());
@@ -1648,7 +1676,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1648
1676
  </div>
1649
1677
  `, p(this, "[data-message]", this.#e.message);
1650
1678
  }
1651
- }, N = class extends HTMLElement {
1679
+ }, xe = class extends HTMLElement {
1652
1680
  #e = !1;
1653
1681
  #t = null;
1654
1682
  #n = null;
@@ -1671,18 +1699,18 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1671
1699
  render() {
1672
1700
  this.className = "block h-full", this.innerHTML = "\n <section class=\"relative h-full min-h-[15rem] overflow-visible rounded-[1.4rem] bg-transparent sm:min-h-[22rem] sm:rounded-[2.2rem] lg:min-h-[32rem] lg:rounded-[2rem]\">\n <div data-board-mount class=\"relative z-10 h-full\"></div>\n <div\n data-placeholder\n class=\"pointer-events-none absolute inset-0 z-20 flex items-center justify-center px-6 text-center text-sm leading-6 text-[var(--lock-muted)]\"\n >\n Board host ready\n </div>\n </section>\n ", this.#n = this.querySelector("[data-board-mount]");
1673
1701
  }
1674
- }, P = (e) => {
1702
+ }, Se = (e) => {
1675
1703
  let t = e.hash.replace(/^#/, "");
1676
1704
  return new URLSearchParams(t || e.search);
1677
- }, Se = (e, t, n) => {
1705
+ }, Ce = (e, t, n) => {
1678
1706
  if (!e) return "";
1679
1707
  let r = new URL(n ?? window.location.href), i = new URLSearchParams();
1680
1708
  return i.set("id", e), t && i.set("url", t), r.hash = i.toString(), r.toString();
1681
- }, Ce = (e) => {
1709
+ }, we = (e) => {
1682
1710
  let t = e.trim();
1683
1711
  if (!t || !t.includes("://")) return null;
1684
1712
  try {
1685
- let e = P(new URL(t)), n = e.get("id");
1713
+ let e = Se(new URL(t)), n = e.get("id");
1686
1714
  return n ? {
1687
1715
  peerId: n,
1688
1716
  signalUrl: e.get("url") ?? ""
@@ -1690,13 +1718,13 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1690
1718
  } catch {
1691
1719
  return null;
1692
1720
  }
1693
- }, we = () => {
1694
- let e = P(new URL(window.location.href));
1721
+ }, Te = () => {
1722
+ let e = Se(new URL(window.location.href));
1695
1723
  return {
1696
1724
  peerId: e.get("id") ?? "",
1697
1725
  signalUrl: e.get("url") ?? ""
1698
1726
  };
1699
- }, Te = {
1727
+ }, Ee = {
1700
1728
  theme: "light",
1701
1729
  gameTitle: "P2P Lockstep",
1702
1730
  signalUrl: "",
@@ -1707,7 +1735,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1707
1735
  registering: !1,
1708
1736
  connecting: !1
1709
1737
  }, F = class extends HTMLElement {
1710
- #e = Te;
1738
+ #e = Ee;
1711
1739
  #t = !1;
1712
1740
  #n = !1;
1713
1741
  connectedCallback() {
@@ -1742,7 +1770,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1742
1770
  i && (i.state = {
1743
1771
  peerId: this.#e.peerId,
1744
1772
  signalUrl: this.#e.signalUrl,
1745
- shareUrl: Se(this.#e.peerId, this.#e.signalUrl)
1773
+ shareUrl: Ce(this.#e.peerId, this.#e.signalUrl)
1746
1774
  });
1747
1775
  }
1748
1776
  #i = (e) => {
@@ -1757,7 +1785,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1757
1785
  return;
1758
1786
  }
1759
1787
  if (t.dataset.field === "target-id") {
1760
- let e = Ce(t.value);
1788
+ let e = we(t.value);
1761
1789
  if (e) {
1762
1790
  l(this, "lockstep-share-detected", e);
1763
1791
  return;
@@ -1766,7 +1794,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1766
1794
  }
1767
1795
  }
1768
1796
  };
1769
- }, Ee = {
1797
+ }, De = {
1770
1798
  theme: "light",
1771
1799
  gameTitle: "P2P Lockstep",
1772
1800
  peerId: "",
@@ -1779,18 +1807,23 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1779
1807
  canStart: !1,
1780
1808
  canUndo: !1,
1781
1809
  canRestart: !1,
1810
+ canOfferDraw: !1,
1811
+ canResign: !1,
1812
+ allowDraw: !1,
1813
+ allowResign: !1,
1782
1814
  started: !1,
1783
1815
  currentTurn: 1,
1784
1816
  turnOwner: null,
1785
1817
  localState: "idle",
1786
1818
  remoteState: "idle",
1787
1819
  pendingAction: null,
1820
+ outcome: null,
1788
1821
  sessionId: "default-session",
1789
1822
  historyLength: 0,
1790
1823
  lastStart: null,
1791
1824
  lastError: ""
1792
- }, De = class extends HTMLElement {
1793
- #e = Ee;
1825
+ }, I = class extends HTMLElement {
1826
+ #e = De;
1794
1827
  #t = !1;
1795
1828
  #n = null;
1796
1829
  #r = null;
@@ -1825,6 +1858,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1825
1858
  readySelf: this.#e.readySelf,
1826
1859
  readyPeer: this.#e.readyPeer,
1827
1860
  pendingAction: this.#e.pendingAction,
1861
+ outcome: this.#e.outcome,
1828
1862
  sessionId: this.#e.sessionId,
1829
1863
  historyLength: this.#e.historyLength,
1830
1864
  lastStart: this.#e.lastStart,
@@ -1836,11 +1870,15 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1836
1870
  canStart: this.#e.canStart,
1837
1871
  canUndo: this.#e.canUndo,
1838
1872
  canRestart: this.#e.canRestart,
1873
+ canOfferDraw: this.#e.canOfferDraw,
1874
+ canResign: this.#e.canResign,
1875
+ allowDraw: this.#e.allowDraw,
1876
+ allowResign: this.#e.allowResign,
1839
1877
  started: this.#e.started,
1840
1878
  connectionState: this.#e.connectionState
1841
1879
  });
1842
1880
  }
1843
- }, I = (e) => JSON.stringify(e), Oe = (e) => {
1881
+ }, L = (e) => JSON.stringify(e), Oe = (e) => {
1844
1882
  if (typeof e != "string") throw TypeError("decode expects a serialized string");
1845
1883
  return JSON.parse(e);
1846
1884
  }, ke = (e) => {
@@ -1859,7 +1897,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1859
1897
  error: e
1860
1898
  };
1861
1899
  }
1862
- }, L = (e, t) => {
1900
+ }, R = (e, t) => {
1863
1901
  t === void 0 ? console.log(e) : console.log(e, t);
1864
1902
  let n = globalThis.__p2p_debug;
1865
1903
  if (typeof n == "function") try {
@@ -1870,36 +1908,36 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1870
1908
  }, Ae = class {
1871
1909
  constructor() {
1872
1910
  this.ws = null, this.peerId = null, this.ready = !1, this.signalHandlers = /* @__PURE__ */ new Set(), this.pendingRegistration = null, this.connect = (e) => new Promise((t, n) => {
1873
- this.ws?.close(), L("[signaling] ws connect", e), this.ws = new WebSocket(e);
1911
+ this.ws?.close(), R("[signaling] ws connect", e), this.ws = new WebSocket(e);
1874
1912
  let r = window.setTimeout(() => {
1875
1913
  try {
1876
1914
  this.ws?.close();
1877
1915
  } catch {}
1878
- L("[signaling] ws open timeout"), n(/* @__PURE__ */ Error("ws open timeout"));
1916
+ R("[signaling] ws open timeout"), n(/* @__PURE__ */ Error("ws open timeout"));
1879
1917
  }, 5e3);
1880
1918
  this.ws.addEventListener("open", () => {
1881
- this.ready = !0, this.registeredPayload = void 0, window.clearTimeout(r), L("[signaling] ws open"), t();
1919
+ this.ready = !0, this.registeredPayload = void 0, window.clearTimeout(r), R("[signaling] ws open"), t();
1882
1920
  }), this.ws.addEventListener("error", (e) => {
1883
- window.clearTimeout(r), L("[signaling] ws error", e), n(/* @__PURE__ */ Error("ws error"));
1921
+ window.clearTimeout(r), R("[signaling] ws error", e), n(/* @__PURE__ */ Error("ws error"));
1884
1922
  }), this.ws.addEventListener("close", (e) => {
1885
- this.ready = !1, this.peerId = null, this.registeredPayload = void 0, this.rejectPendingRegistration(/* @__PURE__ */ Error("ws closed")), window.clearTimeout(r), L("[signaling] ws close", {
1923
+ this.ready = !1, this.peerId = null, this.registeredPayload = void 0, this.rejectPendingRegistration(/* @__PURE__ */ Error("ws closed")), window.clearTimeout(r), R("[signaling] ws close", {
1886
1924
  code: e.code,
1887
1925
  reason: e.reason
1888
1926
  });
1889
1927
  }), this.ws.addEventListener("message", (e) => {
1890
1928
  let t = String(e.data), n = ke(t);
1891
1929
  if (!n.ok) {
1892
- L("[signaling] ws message decode error", t), this.rejectPendingRegistration(/* @__PURE__ */ Error("signaling decode error"));
1930
+ R("[signaling] ws message decode error", t), this.rejectPendingRegistration(/* @__PURE__ */ Error("signaling decode error"));
1893
1931
  return;
1894
1932
  }
1895
1933
  let r = n.value;
1896
- if (L("[signaling] ws message", r), r.type === "ERROR") {
1897
- L("[signaling] error", r), this.rejectPendingRegistration(/* @__PURE__ */ Error("signaling error"));
1934
+ if (R("[signaling] ws message", r), r.type === "ERROR") {
1935
+ R("[signaling] error", r), this.rejectPendingRegistration(/* @__PURE__ */ Error("signaling error"));
1898
1936
  return;
1899
1937
  }
1900
1938
  if ((r.type === "REGISTERED" || r.type === "RESUMED") && (this.peerId = r.to ?? null, this.registeredPayload = r.payload, this.peerId)) {
1901
1939
  let e = this.resolveRegisteredPayload();
1902
- L("[signaling] registered", {
1940
+ R("[signaling] registered", {
1903
1941
  peerId: this.peerId,
1904
1942
  resumeToken: e.resumeToken
1905
1943
  }), this.resolvePendingRegistration({
@@ -1921,14 +1959,14 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1921
1959
  }), this.register = async () => {
1922
1960
  this.assertConnected();
1923
1961
  let e = { type: "REGISTER" };
1924
- L("[signaling] send REGISTER");
1962
+ R("[signaling] send REGISTER");
1925
1963
  let t = this.awaitRegistration("register");
1926
- this.ws?.send(I(e));
1964
+ this.ws?.send(L(e));
1927
1965
  try {
1928
1966
  let e = await t;
1929
- return L("[signaling] register ok", e.peerId), e;
1967
+ return R("[signaling] register ok", e.peerId), e;
1930
1968
  } catch (e) {
1931
- throw L("[signaling] register error", e), e;
1969
+ throw R("[signaling] register error", e), e;
1932
1970
  }
1933
1971
  }, this.resume = async (e) => {
1934
1972
  this.assertConnected();
@@ -1939,14 +1977,14 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1939
1977
  data: e
1940
1978
  }
1941
1979
  };
1942
- L("[signaling] send RESUME", e.peerId);
1980
+ R("[signaling] send RESUME", e.peerId);
1943
1981
  let n = this.awaitRegistration("resume");
1944
- this.ws?.send(I(t));
1982
+ this.ws?.send(L(t));
1945
1983
  try {
1946
1984
  let e = await n;
1947
- return L("[signaling] resume ok", e.peerId), e;
1985
+ return R("[signaling] resume ok", e.peerId), e;
1948
1986
  } catch (e) {
1949
- throw L("[signaling] resume error", e), e;
1987
+ throw R("[signaling] resume error", e), e;
1950
1988
  }
1951
1989
  }, this.relay = (e) => {
1952
1990
  if (!this.ws || !this.ready) return;
@@ -1959,7 +1997,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1959
1997
  to: e.to,
1960
1998
  payload: t
1961
1999
  };
1962
- this.ws.send(I(n));
2000
+ this.ws.send(L(n));
1963
2001
  };
1964
2002
  }
1965
2003
  onSignal(e) {
@@ -1968,6 +2006,9 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1968
2006
  offSignal(e) {
1969
2007
  this.signalHandlers.delete(e);
1970
2008
  }
2009
+ close() {
2010
+ this.rejectPendingRegistration(/* @__PURE__ */ Error("signaling closed")), this.ready = !1, this.peerId = null, this.registeredPayload = void 0, this.ws?.close(), this.ws = null;
2011
+ }
1971
2012
  resolveRegisteredPayload() {
1972
2013
  let e = [], t = "";
1973
2014
  if (this.registeredPayload?.id === "iceServers" && (e = this.registeredPayload.data), this.registeredPayload?.id === "session") {
@@ -1984,12 +2025,13 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
1984
2025
  }
1985
2026
  awaitRegistration(e) {
1986
2027
  return this.pendingRegistration ? Promise.reject(/* @__PURE__ */ Error("registration already pending")) : new Promise((t, n) => {
2028
+ let r = window.setTimeout(() => {
2029
+ this.pendingRegistration = null, R(`[signaling] ${e} timeout`), n(/* @__PURE__ */ Error(`${e} timeout`));
2030
+ }, 5e3);
1987
2031
  this.pendingRegistration = {
1988
2032
  resolve: t,
1989
2033
  reject: n,
1990
- timeoutId: window.setTimeout(() => {
1991
- this.pendingRegistration = null, L(`[signaling] ${e} timeout`), n(/* @__PURE__ */ Error(`${e} timeout`));
1992
- }, 5e3)
2034
+ timeoutId: r
1993
2035
  };
1994
2036
  });
1995
2037
  }
@@ -2088,8 +2130,8 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2088
2130
  let n = Ne.find((n) => n.from === e && n.event === t);
2089
2131
  return n ? n.to : e;
2090
2132
  }, Fe = class {
2091
- constructor(e, t, n, r, i, a, o) {
2092
- this.dc = null, this.pendingSends = [], this.remoteId = null, this.requestedId = null, this.state = "passive", this.localStream = null, this.remoteStream = null, this.onRemoteStreamHandler = null, this.senders = [], this.mediaState = "idle", this.negotiating = !1, this.renegotiateQueued = !1, this.onStateChangeHandler = null, this.onMediaChangeHandler = null, this.handleConnectionStateChange = () => {
2133
+ constructor(e, t, n, r, i, a, o, s = {}) {
2134
+ this.dc = null, this.pendingSends = [], this.remoteId = null, this.requestedId = null, this.state = "passive", this.signalQueue = Promise.resolve(), this.localStream = null, this.remoteStream = null, this.onRemoteStreamHandler = null, this.senders = [], this.mediaState = "idle", this.negotiating = !1, this.renegotiateQueued = !1, this.onStateChangeHandler = null, this.onMediaChangeHandler = null, this.handleConnectionStateChange = () => {
2093
2135
  if (this.pc.connectionState === "connected") {
2094
2136
  this.markConnectedIfReady();
2095
2137
  return;
@@ -2111,6 +2153,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2111
2153
  }, this.handleNegotiationNeeded = () => {
2112
2154
  this.canNegotiate() && this.runTask(this.negotiate(), "negotiate");
2113
2155
  }, this.connect = async (e) => {
2156
+ if (this.boundRemoteId && e !== this.boundRemoteId) throw Error(`RtcPeer is bound to ${this.boundRemoteId}`);
2114
2157
  if (this.state !== "passive") {
2115
2158
  this.requestedId = e, this.disconnect();
2116
2159
  return;
@@ -2119,14 +2162,18 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2119
2162
  }, this.disconnect = () => {
2120
2163
  this.dispatch("DISCONNECT");
2121
2164
  }, this.dispose = () => {
2122
- this.signaling.offSignal(this.onSignalHandler), this.pc.removeEventListener("connectionstatechange", this.handleConnectionStateChange), this.pc.removeEventListener("icecandidate", this.handleIceCandidate), this.pc.removeEventListener("track", this.handleTrackEvent), this.pc.removeEventListener("negotiationneeded", this.handleNegotiationNeeded), this.pc.ondatachannel = null, this.requestedId = null, this.closeConnection(), this.setPeerState("passive"), this.pc.signalingState !== "closed" && this.pc.close();
2165
+ this.subscribesToSignaling && this.signaling.offSignal(this.onSignalHandler), this.pc.removeEventListener("connectionstatechange", this.handleConnectionStateChange), this.pc.removeEventListener("icecandidate", this.handleIceCandidate), this.pc.removeEventListener("track", this.handleTrackEvent), this.pc.removeEventListener("negotiationneeded", this.handleNegotiationNeeded), this.pc.ondatachannel = null, this.requestedId = null, this.closeConnection(), this.setPeerState("passive"), this.pc.signalingState !== "closed" && this.pc.close();
2123
2166
  }, this.send = (e) => {
2124
2167
  if (!this.dc || this.dc.readyState !== "open") {
2125
2168
  this.state === "requesting" && this.pendingSends.push(e);
2126
2169
  return;
2127
2170
  }
2128
2171
  this.dc.send(e);
2129
- }, this.getPeerId = () => this.id, this.getRemoteId = () => this.remoteId, this.getPeerState = () => this.state, this.getMediaState = () => this.mediaState, this.startMedia = (e) => {
2172
+ }, this.getPeerId = () => this.id, this.getRemoteId = () => this.remoteId, this.getPeerState = () => this.state, this.getMediaState = () => this.mediaState, this.receiveSignal = (e) => {
2173
+ this.signalQueue = this.signalQueue.then(() => this.handleSignal(e)).catch((t) => {
2174
+ console.error(`[rtc-peer] signal:${e.type} failed`, t), this.dispatchMedia("DISCONNECT"), this.dispatch("DISCONNECT");
2175
+ });
2176
+ }, this.startMedia = (e) => {
2130
2177
  this.localStream = e, this.dispatchMedia("REQUEST");
2131
2178
  }, this.stopMedia = () => {
2132
2179
  this.localStream = null, this.dispatchMedia("STOP");
@@ -2191,7 +2238,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2191
2238
  this.signaling.relay(t);
2192
2239
  }, this.closeConnection = () => {
2193
2240
  let e = this.dc;
2194
- this.dc = null, this.pendingSends.length = 0, this.negotiating = !1, this.renegotiateQueued = !1, e && (e.onmessage = null, e.onopen = null, e.onclose = null, e.readyState !== "closed" && e.close()), this.dispatchMedia("DISCONNECT"), this.remoteId = null;
2241
+ this.dc = null, this.pendingSends.length = 0, this.negotiating = !1, this.renegotiateQueued = !1, e && (e.onmessage = null, e.onopen = null, e.onclose = null, e.readyState !== "closed" && e.close()), this.dispatchMedia("DISCONNECT"), this.remoteId = this.boundRemoteId;
2195
2242
  }, this.detachLocalMedia = () => {
2196
2243
  if (this.senders.length) {
2197
2244
  for (let e of this.senders) try {
@@ -2237,7 +2284,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2237
2284
  }
2238
2285
  this.dispatchMedia("READY");
2239
2286
  }
2240
- }, this.canNegotiate = () => !!(this.remoteId && this.isMediaReady()), this.shouldHandleSignal = (e) => e.type === "offer" && this.state === "passive" || this.remoteId === e.from, this.setPeerState = (e) => this.state === e ? !1 : (this.state = e, this.onStateChangeHandler?.(this.state), !0), this.setMediaState = (e) => this.mediaState === e ? !1 : (this.mediaState = e, this.onMediaChangeHandler?.(this.mediaState), !0), this.negotiate = async () => {
2287
+ }, this.canNegotiate = () => !!(this.remoteId && this.isMediaReady()), this.shouldHandleSignal = (e) => e.to && e.to !== this.id ? !1 : this.boundRemoteId ? e.from === this.boundRemoteId : e.type === "offer" && this.state === "passive" || this.remoteId === e.from, this.setPeerState = (e) => this.state === e ? !1 : (this.state = e, this.onStateChangeHandler?.(this.state), !0), this.setMediaState = (e) => this.mediaState === e ? !1 : (this.mediaState = e, this.onMediaChangeHandler?.(this.mediaState), !0), this.negotiate = async () => {
2241
2288
  if (this.remoteId) {
2242
2289
  if (this.negotiating) {
2243
2290
  this.renegotiateQueued = !0;
@@ -2258,26 +2305,26 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2258
2305
  this.negotiating = !1, this.renegotiateQueued && (this.renegotiateQueued = !1, this.runTask(this.negotiate(), "negotiate"));
2259
2306
  }
2260
2307
  }
2261
- }, this.id = e, this.pc = t, this.signaling = n, r && (this.onMessageHandler = r), i && (this.onRemoteStreamHandler = i), a && (this.onStateChangeHandler = a), o && (this.onMediaChangeHandler = o), this.onSignalHandler = (e) => {
2262
- this.runTask(this.handleSignal(e), `signal:${e.type}`);
2263
- }, this.signaling.onSignal(this.onSignalHandler), this.pc.addEventListener("connectionstatechange", this.handleConnectionStateChange), this.pc.addEventListener("icecandidate", this.handleIceCandidate), this.pc.ondatachannel = this.handleDataChannel, this.pc.addEventListener("track", this.handleTrackEvent), this.pc.addEventListener("negotiationneeded", this.handleNegotiationNeeded);
2308
+ }, this.id = e, this.pc = t, this.signaling = n, this.boundRemoteId = s.remoteId ?? null, this.remoteId = this.boundRemoteId, this.subscribesToSignaling = s.subscribeToSignaling ?? !0, r && (this.onMessageHandler = r), i && (this.onRemoteStreamHandler = i), a && (this.onStateChangeHandler = a), o && (this.onMediaChangeHandler = o), this.onSignalHandler = (e) => {
2309
+ this.receiveSignal(e);
2310
+ }, this.subscribesToSignaling && this.signaling.onSignal(this.onSignalHandler), this.pc.addEventListener("connectionstatechange", this.handleConnectionStateChange), this.pc.addEventListener("icecandidate", this.handleIceCandidate), this.pc.ondatachannel = this.handleDataChannel, this.pc.addEventListener("track", this.handleTrackEvent), this.pc.addEventListener("negotiationneeded", this.handleNegotiationNeeded);
2264
2311
  }
2265
2312
  runTask(e, t) {
2266
2313
  e.catch((e) => {
2267
2314
  console.error(`[rtc-peer] ${t} failed`, e), this.dispatchMedia("DISCONNECT"), this.dispatch("DISCONNECT");
2268
2315
  });
2269
2316
  }
2270
- }, R = "p2p-lockstep-kit:signal-session", Ie = 600 * 1e3, Le = () => {
2317
+ }, z = "p2p-lockstep-kit:signal-session", Ie = () => {
2271
2318
  if (typeof window > "u") return null;
2272
2319
  try {
2273
2320
  return window.localStorage;
2274
2321
  } catch {
2275
2322
  return null;
2276
2323
  }
2277
- }, Re = () => {
2278
- let e = Le();
2324
+ }, Le = () => {
2325
+ let e = Ie();
2279
2326
  if (!e) return null;
2280
- let t = e.getItem(R);
2327
+ let t = e.getItem(z);
2281
2328
  if (!t) return null;
2282
2329
  try {
2283
2330
  let e = JSON.parse(t);
@@ -2285,34 +2332,33 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2285
2332
  } catch {
2286
2333
  return null;
2287
2334
  }
2288
- }, ze = (e) => {
2289
- let t = Le();
2335
+ }, Re = (e) => {
2336
+ let t = Ie();
2290
2337
  if (t) {
2291
2338
  if (!e) {
2292
- t.removeItem(R);
2339
+ t.removeItem(z);
2293
2340
  return;
2294
2341
  }
2295
- t.setItem(R, JSON.stringify(e));
2342
+ t.setItem(z, JSON.stringify(e));
2296
2343
  }
2297
- }, Be = () => {
2298
- ze(null);
2299
- }, Ve = (e, t = Ie) => Date.now() - e.updatedAt > t, He = class {
2344
+ }, ze = () => {
2345
+ Re(null);
2346
+ }, Be = class {
2300
2347
  constructor(e = new Ae()) {
2301
2348
  this.peer = null, this.onMessageHandler = null, this.onRemoteStreamHandler = null, this.pendingMediaStream = null, this.onStateChangeHandler = null, this.onMediaChangeHandler = null, this.getLocalPeerId = () => this.peer?.getPeerId() ?? null, this.getRemotePeerId = () => this.peer?.getRemoteId() ?? null, this.peerState = () => this.peer?.getPeerState() ?? "passive", this.mediaState = () => this.peer?.getMediaState() ?? "idle", this.signaling = e;
2302
2349
  }
2303
2350
  async register(e) {
2304
2351
  this.peer?.dispose(), this.peer = null, await this.signaling.connect(e);
2305
- let t = Re(), n = null;
2306
- if (t && Ve(t)) Be();
2307
- else if (t) try {
2352
+ let t = Le(), n = null;
2353
+ if (t) try {
2308
2354
  n = await this.signaling.resume({
2309
2355
  peerId: t.peerId,
2310
2356
  resumeToken: t.resumeToken
2311
2357
  });
2312
2358
  } catch {
2313
- Be();
2359
+ ze();
2314
2360
  }
2315
- n ||= await this.signaling.register(), n.resumeToken && ze({
2361
+ n ||= await this.signaling.register(), n.resumeToken && Re({
2316
2362
  peerId: n.peerId,
2317
2363
  resumeToken: n.resumeToken,
2318
2364
  updatedAt: Date.now()
@@ -2337,7 +2383,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2337
2383
  this.peer && await this.peer.connect(e);
2338
2384
  }
2339
2385
  send(e) {
2340
- let t = I(e);
2386
+ let t = L(e);
2341
2387
  this.peer?.send(t);
2342
2388
  }
2343
2389
  disconnect() {
@@ -2361,19 +2407,19 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2361
2407
  onMediaChange(e) {
2362
2408
  this.onMediaChangeHandler = e, e(this.peer?.getMediaState() ?? "idle");
2363
2409
  }
2364
- }, z = (e) => (t, n) => {
2410
+ }, B = (e) => (t, n) => {
2365
2411
  let r = e === "debug" ? console.info : console[e];
2366
2412
  if (n !== void 0) {
2367
2413
  r(t, n);
2368
2414
  return;
2369
2415
  }
2370
2416
  r(t);
2371
- }, B = {
2372
- debug: z("debug"),
2373
- info: z("info"),
2374
- warn: z("warn"),
2375
- error: z("error")
2376
- }, Ue = (e) => {
2417
+ }, V = {
2418
+ debug: B("debug"),
2419
+ info: B("info"),
2420
+ warn: B("warn"),
2421
+ error: B("error")
2422
+ }, Ve = (e) => {
2377
2423
  if (typeof e != "string") return {
2378
2424
  ok: !1,
2379
2425
  error: /* @__PURE__ */ TypeError("decodeSafe expects a serialized string")
@@ -2389,11 +2435,11 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2389
2435
  error: e
2390
2436
  };
2391
2437
  }
2392
- }, We = (e) => {
2438
+ }, He = (e) => {
2393
2439
  if (typeof e != "string") return !e || typeof e != "object" ? null : e;
2394
- let t = Ue(e);
2440
+ let t = Ve(e);
2395
2441
  return !t.ok || !t.value || typeof t.value != "object" ? null : t.value;
2396
- }, Ge = class {
2442
+ }, Ue = class {
2397
2443
  handlers = {};
2398
2444
  processingQueue = Promise.resolve();
2399
2445
  emit(e, t, n = "local") {
@@ -2404,11 +2450,11 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2404
2450
  });
2405
2451
  }
2406
2452
  register(e, t) {
2407
- this.handlers[e] = t, B.debug(`[session:bus] registered ${e}`);
2453
+ this.handlers[e] = t, V.debug(`[session:bus] registered ${e}`);
2408
2454
  }
2409
2455
  dispatch(e) {
2410
2456
  this.processingQueue = this.processingQueue.then(async () => {
2411
- B.debug(`[session:bus] dispatch ${e.type}`, {
2457
+ V.debug(`[session:bus] dispatch ${e.type}`, {
2412
2458
  from: e.from,
2413
2459
  payload: e.payload,
2414
2460
  turn: e.turn,
@@ -2417,16 +2463,16 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2417
2463
  let t = this.handlers[e.type];
2418
2464
  if (t) {
2419
2465
  try {
2420
- await t(e), B.debug(`[session:bus] handled ${e.type}`, { from: e.from });
2466
+ await t(e), V.debug(`[session:bus] handled ${e.type}`, { from: e.from });
2421
2467
  } catch (t) {
2422
2468
  console.error(`[CommandBus] Error in ${e.type}:`, t);
2423
2469
  }
2424
2470
  return;
2425
2471
  }
2426
- B.debug(`[session:bus] no handler for ${e.type}`, { from: e.from });
2472
+ V.debug(`[session:bus] no handler for ${e.type}`, { from: e.from });
2427
2473
  });
2428
2474
  }
2429
- }, V = [
2475
+ }, H = [
2430
2476
  {
2431
2477
  from: "idle",
2432
2478
  event: "READY",
@@ -2517,6 +2563,16 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2517
2563
  event: "RESTART",
2518
2564
  to: "waiting_approval"
2519
2565
  },
2566
+ {
2567
+ from: "turn",
2568
+ event: "REQUEST",
2569
+ to: "waiting_approval"
2570
+ },
2571
+ {
2572
+ from: "remote_turn",
2573
+ event: "REQUEST",
2574
+ to: "waiting_approval"
2575
+ },
2520
2576
  {
2521
2577
  from: "turn",
2522
2578
  event: "REMOTE_UNDO",
@@ -2537,6 +2593,16 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2537
2593
  event: "REMOTE_RESTART",
2538
2594
  to: "approving"
2539
2595
  },
2596
+ {
2597
+ from: "turn",
2598
+ event: "REMOTE_REQUEST",
2599
+ to: "approving"
2600
+ },
2601
+ {
2602
+ from: "remote_turn",
2603
+ event: "REMOTE_REQUEST",
2604
+ to: "approving"
2605
+ },
2540
2606
  {
2541
2607
  from: "waiting_approval",
2542
2608
  event: "APPROVE",
@@ -2577,6 +2643,21 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2577
2643
  event: "GAME_OVER",
2578
2644
  to: "idle"
2579
2645
  },
2646
+ {
2647
+ from: "waiting_approval",
2648
+ event: "GAME_OVER",
2649
+ to: "idle"
2650
+ },
2651
+ {
2652
+ from: "approving",
2653
+ event: "GAME_OVER",
2654
+ to: "idle"
2655
+ },
2656
+ {
2657
+ from: "syncing",
2658
+ event: "GAME_OVER",
2659
+ to: "idle"
2660
+ },
2580
2661
  {
2581
2662
  from: "turn",
2582
2663
  event: "SYNC",
@@ -2667,13 +2748,13 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2667
2748
  event: "ONLINE",
2668
2749
  to: "syncing"
2669
2750
  }
2670
- ], Ke = (e, t, n) => {
2671
- if (n) return V.find((r) => r.from === e && r.event === t && r.to === n) ? n : e;
2751
+ ], We = (e, t, n) => {
2752
+ if (n) return H.find((r) => r.from === e && r.event === t && r.to === n) ? n : e;
2672
2753
  {
2673
- let n = V.find((n) => n.from === e && n.event === t);
2754
+ let n = H.find((n) => n.from === e && n.event === t);
2674
2755
  return n ? n.to : e;
2675
2756
  }
2676
- }, qe = (e, t, n) => n ? !!V.find((r) => r.from === e && r.event === t && r.to === n) : !!V.find((n) => n.from === e && n.event === t), H = class {
2757
+ }, Ge = (e, t, n) => n ? !!H.find((r) => r.from === e && r.event === t && r.to === n) : !!H.find((n) => n.from === e && n.event === t), U = class {
2677
2758
  state;
2678
2759
  constructor(e = "idle") {
2679
2760
  this.state = e;
@@ -2682,19 +2763,19 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2682
2763
  return this.state;
2683
2764
  }
2684
2765
  hasNextState(e, t) {
2685
- return qe(this.state, e, t);
2766
+ return Ge(this.state, e, t);
2686
2767
  }
2687
2768
  dispatch(e, t) {
2688
- this.state = Ke(this.state, e, t);
2769
+ this.state = We(this.state, e, t);
2689
2770
  }
2690
- }, Je = class {
2771
+ }, Ke = class {
2691
2772
  validateMove() {
2692
2773
  return { valid: !0 };
2693
2774
  }
2694
2775
  checkWin() {
2695
2776
  return null;
2696
2777
  }
2697
- }, Ye = class {
2778
+ }, qe = class {
2698
2779
  observers = /* @__PURE__ */ new Set();
2699
2780
  subscribe(e) {
2700
2781
  this.observers.add(e);
@@ -2723,7 +2804,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2723
2804
  console.error("[StateObserver]", e);
2724
2805
  }
2725
2806
  }
2726
- }, Xe = class {
2807
+ }, Je = class {
2727
2808
  observers = /* @__PURE__ */ new Set();
2728
2809
  currentSnapshot = null;
2729
2810
  subscribe(e) {
@@ -2771,7 +2852,7 @@ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescr
2771
2852
  return this.observers.size;
2772
2853
  }
2773
2854
  };
2774
- function Ze(e, t = !1) {
2855
+ function W(e, t = !1) {
2775
2856
  return {
2776
2857
  localState: e.getState("local"),
2777
2858
  remoteState: e.getState("remote"),
@@ -2779,10 +2860,14 @@ function Ze(e, t = !1) {
2779
2860
  history: e.getHistory(),
2780
2861
  lastStart: e.getLastStart(),
2781
2862
  pendingAction: e.getPendingAction(),
2863
+ outcome: e.getOutcome(),
2782
2864
  connected: t
2783
2865
  };
2784
2866
  }
2785
- var Qe = class {
2867
+ var Ye = class {
2868
+ stateRef;
2869
+ uiObserver;
2870
+ getConnected;
2786
2871
  lastNotificationTime = 0;
2787
2872
  notificationThrottleMs = 0;
2788
2873
  constructor(e, t, n = () => !1) {
@@ -2792,8 +2877,8 @@ var Qe = class {
2792
2877
  let e = Date.now();
2793
2878
  if (this.lastNotificationTime + this.notificationThrottleMs > e) return;
2794
2879
  this.lastNotificationTime = e;
2795
- let t = Ze(this.stateRef, this.getConnected());
2796
- B.debug("[session:observer] state snapshot", {
2880
+ let t = W(this.stateRef, this.getConnected());
2881
+ V.debug("[session:observer] state snapshot", {
2797
2882
  local: t.localState,
2798
2883
  remote: t.remoteState,
2799
2884
  turn: t.turn,
@@ -2811,9 +2896,9 @@ var Qe = class {
2811
2896
  emitEvent(e) {
2812
2897
  this.uiObserver.notifyGameEvent(e);
2813
2898
  }
2814
- }, $e = class {
2815
- local = new H("idle");
2816
- remote = new H("idle");
2899
+ }, Xe = class {
2900
+ local = new U("idle");
2901
+ remote = new U("idle");
2817
2902
  localId = null;
2818
2903
  remoteId = null;
2819
2904
  history = [];
@@ -2821,10 +2906,11 @@ var Qe = class {
2821
2906
  pendingUndoCount = null;
2822
2907
  resumeTurn = null;
2823
2908
  lastStart = null;
2824
- gamePlugin = new Je();
2825
- stateObserverManager = new Ye();
2909
+ outcome = null;
2910
+ gamePlugin = new Ke();
2911
+ stateObserverManager = new qe();
2826
2912
  constructor(e, t) {
2827
- e && (this.localId = e), t && (this.remoteId = t), B.debug("[session:state] created", {
2913
+ e && (this.localId = e), t && (this.remoteId = t), V.debug("[session:state] created", {
2828
2914
  localId: e,
2829
2915
  remoteId: t
2830
2916
  });
@@ -2839,7 +2925,7 @@ var Qe = class {
2839
2925
  return this.remoteId;
2840
2926
  }
2841
2927
  setremoteId(e) {
2842
- this.remoteId = e, B.debug("[session:state] remote id set", { remoteId: e });
2928
+ this.remoteId = e, V.debug("[session:state] remote id set", { remoteId: e });
2843
2929
  }
2844
2930
  getState(e) {
2845
2931
  return this.getPlayerFsm(e).getState();
@@ -2851,7 +2937,7 @@ var Qe = class {
2851
2937
  return this.history.slice();
2852
2938
  }
2853
2939
  replaceHistory(e) {
2854
- B.debug("[session:history] replace", { count: e.length }), this.clearHistory(), e.forEach((e) => {
2940
+ V.debug("[session:history] replace", { count: e.length }), this.clearHistory(), e.forEach((e) => {
2855
2941
  this.pushHistory({
2856
2942
  turn: e.turn,
2857
2943
  player: e.player,
@@ -2861,10 +2947,10 @@ var Qe = class {
2861
2947
  }
2862
2948
  clearHistory() {
2863
2949
  let e = this.history.length;
2864
- this.history.splice(0, this.history.length), B.debug("[session:history] clear", { count: e }), this.notifyHistoryChanged();
2950
+ this.history.splice(0, this.history.length), V.debug("[session:history] clear", { count: e }), this.notifyHistoryChanged();
2865
2951
  }
2866
2952
  pushHistory(e) {
2867
- this.history.push(e), B.debug("[session:history] push", {
2953
+ this.history.push(e), V.debug("[session:history] push", {
2868
2954
  turn: e.turn,
2869
2955
  player: e.player,
2870
2956
  move: e.move,
@@ -2873,7 +2959,7 @@ var Qe = class {
2873
2959
  }
2874
2960
  popHistory() {
2875
2961
  let e = this.history.pop() ?? null;
2876
- return e && (B.debug("[session:history] pop", {
2962
+ return e && (V.debug("[session:history] pop", {
2877
2963
  turn: e.turn,
2878
2964
  player: e.player,
2879
2965
  move: e.move,
@@ -2887,7 +2973,7 @@ var Qe = class {
2887
2973
  let r = this.getState(e);
2888
2974
  this.getPlayerFsm(e).dispatch(t, n);
2889
2975
  let i = this.getState(e);
2890
- B.debug(`[session:fsm] ${e} ${t}`, {
2976
+ V.debug(`[session:fsm] ${e} ${t}`, {
2891
2977
  from: r,
2892
2978
  to: i,
2893
2979
  requested: n,
@@ -2899,7 +2985,7 @@ var Qe = class {
2899
2985
  }), this.notifyStateChanged();
2900
2986
  }
2901
2987
  setPendingAction(e) {
2902
- B.debug("[session:state] pending action set", {
2988
+ V.debug("[session:state] pending action set", {
2903
2989
  from: this.pendingAction,
2904
2990
  to: e
2905
2991
  }), this.pendingAction = e;
@@ -2908,7 +2994,7 @@ var Qe = class {
2908
2994
  return this.pendingAction;
2909
2995
  }
2910
2996
  setPendingUndoCount(e) {
2911
- B.debug("[session:state] pending undo count set", {
2997
+ V.debug("[session:state] pending undo count set", {
2912
2998
  from: this.pendingUndoCount,
2913
2999
  to: e
2914
3000
  }), this.pendingUndoCount = e;
@@ -2917,7 +3003,7 @@ var Qe = class {
2917
3003
  return this.pendingUndoCount;
2918
3004
  }
2919
3005
  setLastStart(e) {
2920
- B.debug("[session:state] last start set", {
3006
+ V.debug("[session:state] last start set", {
2921
3007
  from: this.lastStart,
2922
3008
  to: e
2923
3009
  }), this.lastStart = e;
@@ -2926,7 +3012,7 @@ var Qe = class {
2926
3012
  return this.lastStart;
2927
3013
  }
2928
3014
  setResumeTurn(e) {
2929
- B.debug("[session:state] resume turn set", {
3015
+ V.debug("[session:state] resume turn set", {
2930
3016
  from: this.resumeTurn,
2931
3017
  to: e
2932
3018
  }), this.resumeTurn = e;
@@ -2934,11 +3020,20 @@ var Qe = class {
2934
3020
  getResumeTurn() {
2935
3021
  return this.resumeTurn;
2936
3022
  }
3023
+ setOutcome(e) {
3024
+ V.debug("[session:state] outcome set", {
3025
+ from: this.outcome,
3026
+ to: e
3027
+ }), this.outcome = e, this.notifyStateChanged();
3028
+ }
3029
+ getOutcome() {
3030
+ return this.outcome;
3031
+ }
2937
3032
  getPlayerFsm(e) {
2938
3033
  return e === "local" ? this.local : this.remote;
2939
3034
  }
2940
3035
  notifyStateChanged() {
2941
- B.debug("[session:state] notify state changed", {
3036
+ V.debug("[session:state] notify state changed", {
2942
3037
  local: this.getState("local"),
2943
3038
  remote: this.getState("remote"),
2944
3039
  turn: this.getTurnCount(),
@@ -2947,21 +3042,21 @@ var Qe = class {
2947
3042
  }), this.stateObserverManager.notifyStateChanged();
2948
3043
  }
2949
3044
  notifyHistoryChanged() {
2950
- B.debug("[session:state] notify history changed", {
3045
+ V.debug("[session:state] notify history changed", {
2951
3046
  turn: this.getTurnCount(),
2952
3047
  history: this.history.length,
2953
3048
  pending: this.pendingAction
2954
3049
  }), this.stateObserverManager.notifyHistoryChanged();
2955
3050
  }
2956
3051
  notifyGameReset() {
2957
- B.debug("[session:state] notify game reset"), this.stateObserverManager.notifyGameReset();
3052
+ V.debug("[session:state] notify game reset"), this.stateObserverManager.notifyGameReset();
2958
3053
  }
2959
3054
  dispatchPair(e, t, n, r) {
2960
3055
  let i = {
2961
3056
  local: this.local.getState(),
2962
3057
  remote: this.remote.getState()
2963
3058
  };
2964
- this.local.dispatch(e, t), this.remote.dispatch(n, r), B.debug("[session:fsm] pair dispatch", {
3059
+ this.local.dispatch(e, t), this.remote.dispatch(n, r), V.debug("[session:fsm] pair dispatch", {
2965
3060
  before: i,
2966
3061
  after: {
2967
3062
  local: this.local.getState(),
@@ -2978,32 +3073,38 @@ var Qe = class {
2978
3073
  }
2979
3074
  gameSnapshot = null;
2980
3075
  saveGameSnapshot(e) {
2981
- this.gameSnapshot = e, B.debug("[session:state] game snapshot saved", { snapshot: e });
3076
+ this.gameSnapshot = e, V.debug("[session:state] game snapshot saved", { snapshot: e });
2982
3077
  }
2983
3078
  getGameSnapshot() {
2984
3079
  return this.gameSnapshot;
2985
3080
  }
2986
3081
  clearGameSnapshot() {
2987
- this.gameSnapshot = null, B.debug("[session:state] game snapshot cleared");
3082
+ this.gameSnapshot = null, V.debug("[session:state] game snapshot cleared");
2988
3083
  }
2989
3084
  hasPendingAction() {
2990
3085
  return this.pendingAction !== null;
2991
3086
  }
2992
3087
  clearPendingStates() {
2993
- B.debug("[session:state] pending states cleared", {
3088
+ V.debug("[session:state] pending states cleared", {
2994
3089
  pending: this.pendingAction,
2995
3090
  pendingUndoCount: this.pendingUndoCount,
2996
3091
  resumeTurn: this.resumeTurn
2997
3092
  }), this.pendingAction = null, this.pendingUndoCount = null, this.resumeTurn = null, this.notifyStateChanged();
2998
3093
  }
2999
3094
  initializeUndoRequest(e, t) {
3000
- this.pendingAction = "undo", this.pendingUndoCount = e, this.resumeTurn = t, B.debug("[session:state] undo request initialized", {
3095
+ this.pendingAction = "undo", this.pendingUndoCount = e, this.resumeTurn = t, V.debug("[session:state] undo request initialized", {
3001
3096
  undoCount: e,
3002
3097
  resumeTurn: t
3003
3098
  });
3004
3099
  }
3005
3100
  initializeRestartRequest(e) {
3006
- this.pendingAction = "restart", this.resumeTurn = e, B.debug("[session:state] restart request initialized", { resumeTurn: e });
3101
+ this.pendingAction = "restart", this.resumeTurn = e, V.debug("[session:state] restart request initialized", { resumeTurn: e });
3102
+ }
3103
+ initializePendingRequest(e, t) {
3104
+ this.pendingAction = e, this.resumeTurn = t, V.debug("[session:state] approval request initialized", {
3105
+ action: e,
3106
+ resumeTurn: t
3107
+ });
3007
3108
  }
3008
3109
  isPendingUndo() {
3009
3110
  return this.pendingAction === "undo";
@@ -3012,20 +3113,20 @@ var Qe = class {
3012
3113
  return this.pendingAction === "restart";
3013
3114
  }
3014
3115
  applyUndo(e = 1) {
3015
- B.debug("[session:history] apply undo", { count: e });
3116
+ V.debug("[session:history] apply undo", { count: e });
3016
3117
  for (let t = 0; t < e; t++) this.popHistory();
3017
3118
  }
3018
3119
  resetGame() {
3019
- B.debug("[session:state] reset game", {
3120
+ V.debug("[session:state] reset game", {
3020
3121
  local: this.getState("local"),
3021
3122
  remote: this.getState("remote"),
3022
3123
  history: this.history.length,
3023
3124
  lastStart: this.lastStart,
3024
3125
  pending: this.pendingAction
3025
- }), this.clearHistory(), this.local = new H("idle"), this.remote = new H("idle"), this.lastStart = null, this.pendingAction = null, this.pendingUndoCount = null, this.resumeTurn = null, this.notifyGameReset(), this.notifyStateChanged();
3126
+ }), this.clearHistory(), this.local = new U("idle"), this.remote = new U("idle"), this.lastStart = null, this.outcome = null, this.pendingAction = null, this.pendingUndoCount = null, this.resumeTurn = null, this.notifyGameReset(), this.notifyStateChanged();
3026
3127
  }
3027
3128
  recordStartPlayer(e) {
3028
- this.lastStart = e, B.debug("[session:state] start player recorded", { player: e });
3129
+ this.lastStart = e, V.debug("[session:state] start player recorded", { player: e });
3029
3130
  }
3030
3131
  getLastMove() {
3031
3132
  return this.history.length > 0 ? this.history[this.history.length - 1] : null;
@@ -3047,7 +3148,7 @@ var Qe = class {
3047
3148
  remote: this.remote.getState(),
3048
3149
  lastStart: this.lastStart
3049
3150
  };
3050
- e === "local" ? (this.local.dispatch("START", "turn"), this.remote.dispatch("START", "remote_turn"), this.lastStart = "local") : (this.local.dispatch("START", "remote_turn"), this.remote.dispatch("START", "turn"), this.lastStart = "remote"), B.debug("[session:fsm] start dispatch", {
3151
+ e === "local" ? (this.local.dispatch("START", "turn"), this.remote.dispatch("START", "remote_turn"), this.lastStart = "local") : (this.local.dispatch("START", "remote_turn"), this.remote.dispatch("START", "turn"), this.lastStart = "remote"), V.debug("[session:fsm] start dispatch", {
3051
3152
  before: t,
3052
3153
  firstPlayer: e,
3053
3154
  after: {
@@ -3061,7 +3162,7 @@ var Qe = class {
3061
3162
  e === "local" ? this.dispatchPair("SYNC_COMPLETE", "turn", "SYNC_COMPLETE", "remote_turn") : this.dispatchPair("SYNC_COMPLETE", "remote_turn", "SYNC_COMPLETE", "turn"), this.resumeTurn = null;
3062
3163
  }
3063
3164
  setGamePlugin(e) {
3064
- this.gamePlugin = e, B.debug("[session:plugin] game plugin set", {
3165
+ this.gamePlugin = e, V.debug("[session:plugin] game plugin set", {
3065
3166
  hasInitialize: !!e.initialize,
3066
3167
  hasCleanup: !!e.cleanup
3067
3168
  }), e.initialize && e.initialize();
@@ -3071,7 +3172,7 @@ var Qe = class {
3071
3172
  }
3072
3173
  validateMove(e) {
3073
3174
  let t = this.buildGameState(), n = this.gamePlugin.validateMove(e, t);
3074
- return B.debug("[session:plugin] validate move", {
3175
+ return V.debug("[session:plugin] validate move", {
3075
3176
  move: e,
3076
3177
  result: n,
3077
3178
  local: t.localState,
@@ -3082,14 +3183,17 @@ var Qe = class {
3082
3183
  }
3083
3184
  checkWin() {
3084
3185
  let e = this.buildGameState(), t = this.gamePlugin.checkWin(e, this.getHistory());
3085
- return B.debug("[session:plugin] check win", {
3186
+ return V.debug("[session:plugin] check win", {
3086
3187
  winner: t,
3087
3188
  turn: e.turn,
3088
3189
  history: e.history.length
3089
3190
  }), t;
3090
3191
  }
3192
+ completeGame(e) {
3193
+ this.setOutcome(e), this.canAction("local", "GAME_OVER") && this.dispatch("local", "GAME_OVER"), this.canAction("remote", "GAME_OVER") && this.dispatch("remote", "GAME_OVER"), this.clearPendingStates(), this.cleanupGame();
3194
+ }
3091
3195
  cleanupGame() {
3092
- this.gamePlugin.cleanup && this.gamePlugin.cleanup(), B.debug("[session:plugin] cleanup game");
3196
+ this.gamePlugin.cleanup && this.gamePlugin.cleanup(), V.debug("[session:plugin] cleanup game");
3093
3197
  }
3094
3198
  buildGameState() {
3095
3199
  return {
@@ -3100,7 +3204,9 @@ var Qe = class {
3100
3204
  lastStart: this.getLastStart()
3101
3205
  };
3102
3206
  }
3103
- }, et = class {
3207
+ }, Ze = class {
3208
+ client;
3209
+ bus;
3104
3210
  localPeerId;
3105
3211
  remotePeerId;
3106
3212
  isConnected = !1;
@@ -3108,7 +3214,7 @@ var Qe = class {
3108
3214
  mediaStateListener = () => {};
3109
3215
  constructor(e, t, n) {
3110
3216
  this.client = e, this.bus = t, this.localPeerId = n ?? null, this.remotePeerId = null, this.client.onMessage((e) => {
3111
- let t = We(e);
3217
+ let t = He(e);
3112
3218
  !t || typeof t != "object" || !t.type || this.bus.dispatch({
3113
3219
  ...t,
3114
3220
  type: t.type,
@@ -3151,7 +3257,7 @@ var Qe = class {
3151
3257
  onMediaStateChange(e) {
3152
3258
  this.mediaStateListener = e;
3153
3259
  }
3154
- }, tt = (e, t, n) => new et(e, t, n), nt = class {
3260
+ }, Qe = (e, t, n) => new Ze(e, t, n), $e = class {
3155
3261
  state;
3156
3262
  bus;
3157
3263
  net;
@@ -3171,14 +3277,14 @@ var Qe = class {
3171
3277
  getSid() {
3172
3278
  return this.sid;
3173
3279
  }
3174
- }, U = null, rt = (e, t, n, r) => {
3175
- U = new nt(e, t, n, r);
3176
- }, W = () => {
3177
- if (!U) throw Error("[SessionContext] Not initialized. Call initializeContext() first.");
3178
- return U;
3179
- }, G = () => W().getState(), K = () => W().getBus(), it = () => W().getSid(), q = (e) => W().getNet().send(e), at = (e) => {
3180
- let t = G(), n = K(), r = it();
3181
- if (B.debug("[session:ready] received", {
3280
+ }, G = null, et = (e, t, n, r) => {
3281
+ G = new $e(e, t, n, r);
3282
+ }, K = () => {
3283
+ if (!G) throw Error("[SessionContext] Not initialized. Call initializeContext() first.");
3284
+ return G;
3285
+ }, q = () => K().getState(), J = () => K().getBus(), tt = () => K().getSid(), Y = (e) => K().getNet().send(e), nt = (e) => {
3286
+ let t = q(), n = J(), r = tt();
3287
+ if (V.debug("[session:ready] received", {
3182
3288
  from: e.from,
3183
3289
  sid: e.sid,
3184
3290
  localSid: r,
@@ -3189,10 +3295,10 @@ var Qe = class {
3189
3295
  console.warn("[Ready] Cannot dispatch READY from current state", { state: t.getState("local") });
3190
3296
  return;
3191
3297
  }
3192
- t.dispatch("local", "READY"), t.dispatch("remote", "REMOTE_READY"), q({
3298
+ t.dispatch("local", "READY"), t.dispatch("remote", "REMOTE_READY"), Y({
3193
3299
  type: "READY",
3194
3300
  sid: r
3195
- }), B.debug("[session:ready] local toggled", {
3301
+ }), V.debug("[session:ready] local toggled", {
3196
3302
  local: t.getState("local"),
3197
3303
  remote: t.getState("remote")
3198
3304
  });
@@ -3210,13 +3316,13 @@ var Qe = class {
3210
3316
  console.warn("[Ready] Cannot dispatch READY for remote peer", { state: t.getState("remote") });
3211
3317
  return;
3212
3318
  }
3213
- t.dispatch("remote", "READY"), t.dispatch("local", "REMOTE_READY"), B.debug("[session:ready] remote toggled", {
3319
+ t.dispatch("remote", "READY"), t.dispatch("local", "REMOTE_READY"), V.debug("[session:ready] remote toggled", {
3214
3320
  local: t.getState("local"),
3215
3321
  remote: t.getState("remote")
3216
3322
  });
3217
- }, ot = (e) => e ? e === "local" ? "remote" : "local" : Math.random() < .5 ? "local" : "remote", st = (e) => {
3218
- let t = G();
3219
- if (B.debug("[session:start] received", {
3323
+ }, rt = (e) => e ? e === "local" ? "remote" : "local" : Math.random() < .5 ? "local" : "remote", it = (e) => {
3324
+ let t = q();
3325
+ if (V.debug("[session:start] received", {
3220
3326
  from: e.from,
3221
3327
  payload: e.payload,
3222
3328
  local: t.getState("local"),
@@ -3230,11 +3336,11 @@ var Qe = class {
3230
3336
  });
3231
3337
  return;
3232
3338
  }
3233
- let e = ot(t.getLastStart()), n = e === "local" ? "turn" : "remote_turn", r = e === "local" ? "remote_turn" : "turn";
3234
- t.getHistory().length > 0 && (t.clearHistory(), B.debug("[session:start] cleared previous match history")), t.setLastStart(e), t.dispatch("local", "START", n), t.dispatch("remote", "REMOTE_START", r), q({
3339
+ let e = rt(t.getLastStart()), n = e === "local" ? "turn" : "remote_turn", r = e === "local" ? "remote_turn" : "turn";
3340
+ t.getHistory().length > 0 && (t.clearHistory(), V.debug("[session:start] cleared previous match history")), t.setOutcome(null), t.setLastStart(e), t.dispatch("local", "START", n), t.dispatch("remote", "REMOTE_START", r), Y({
3235
3341
  type: "START",
3236
3342
  payload: { starter: e === "local" ? "sender" : "receiver" }
3237
- }), B.debug("[session:start] local started", { nextStarter: e });
3343
+ }), V.debug("[session:start] local started", { nextStarter: e });
3238
3344
  return;
3239
3345
  }
3240
3346
  let n = e.payload?.starter;
@@ -3250,10 +3356,10 @@ var Qe = class {
3250
3356
  return;
3251
3357
  }
3252
3358
  let r = n === "sender" ? "remote" : "local", i = r === "local" ? "turn" : "remote_turn", a = r === "local" ? "remote_turn" : "turn";
3253
- t.getHistory().length > 0 && (t.clearHistory(), B.debug("[session:start] cleared previous match history")), t.setLastStart(r), t.dispatch("local", "REMOTE_START", i), t.dispatch("remote", "START", a), B.debug("[session:start] remote started", { starter: r });
3254
- }, ct = (e) => {
3255
- let t = G(), n = e.payload;
3256
- if (B.debug("[session:move] received", {
3359
+ t.getHistory().length > 0 && (t.clearHistory(), V.debug("[session:start] cleared previous match history")), t.setOutcome(null), t.setLastStart(r), t.dispatch("local", "REMOTE_START", i), t.dispatch("remote", "START", a), V.debug("[session:start] remote started", { starter: r });
3360
+ }, at = (e) => {
3361
+ let t = q(), n = e.payload;
3362
+ if (V.debug("[session:move] received", {
3257
3363
  from: e.from,
3258
3364
  payload: n,
3259
3365
  local: t.getState("local"),
@@ -3279,20 +3385,24 @@ var Qe = class {
3279
3385
  turn: r,
3280
3386
  player: "local",
3281
3387
  move: n
3282
- }), q({
3388
+ }), Y({
3283
3389
  type: "MOVE",
3284
3390
  turn: r,
3285
3391
  payload: n
3286
- }), B.debug("[session:move] local move sent", {
3392
+ }), V.debug("[session:move] local move sent", {
3287
3393
  turn: r,
3288
3394
  payload: n
3289
3395
  });
3290
3396
  let i = t.checkWin();
3291
3397
  if (i) {
3292
- B.debug("[session:move] game over detected", {
3398
+ V.debug("[session:move] game over detected", {
3293
3399
  winner: i,
3294
3400
  turn: r
3295
- }), t.dispatch("local", "GAME_OVER"), t.dispatch("remote", "GAME_OVER"), t.cleanupGame(), B.debug("[session:move] local game over applied", {
3401
+ }), t.completeGame({
3402
+ kind: "win",
3403
+ winner: i,
3404
+ reason: "rules"
3405
+ }), V.debug("[session:move] local game over applied", {
3296
3406
  winner: i,
3297
3407
  turn: r
3298
3408
  });
@@ -3321,23 +3431,33 @@ var Qe = class {
3321
3431
  });
3322
3432
  let a = t.checkWin();
3323
3433
  if (a) {
3324
- B.debug("[session:move] game over detected", {
3434
+ V.debug("[session:move] game over detected", {
3325
3435
  winner: a,
3326
3436
  turn: i
3327
- }), t.dispatch("local", "GAME_OVER"), t.dispatch("remote", "GAME_OVER"), t.cleanupGame(), B.debug("[session:move] remote game over applied", {
3437
+ }), t.completeGame({
3438
+ kind: "win",
3439
+ winner: a,
3440
+ reason: "rules"
3441
+ }), V.debug("[session:move] remote game over applied", {
3328
3442
  winner: a,
3329
3443
  turn: i
3330
3444
  });
3331
3445
  return;
3332
3446
  }
3333
- B.debug("[session:move] remote move applied", {
3447
+ V.debug("[session:move] remote move applied", {
3334
3448
  turn: i,
3335
3449
  payload: n
3336
3450
  });
3337
- }, lt = (e) => e === "undo" || e === "restart", ut = (e) => {
3451
+ }, ot = (e) => e === "undo" || e === "restart" || e === "draw", st = (e) => {
3452
+ let t = q();
3453
+ e === "undo" ? t.applyUndo(t.getPendingUndoCount() ?? 1) : e === "restart" ? t.resetGame() : t.completeGame({
3454
+ kind: "draw",
3455
+ reason: "agreement"
3456
+ });
3457
+ }, ct = (e) => {
3338
3458
  if (e.type !== "APPROVE" && e.type !== "REJECT") return;
3339
- let t = G(), n = e.payload, r = t.getPendingAction(), i = r ?? (e.from === "local" && e.type === "REJECT" && lt(n?.action) ? n.action : null);
3340
- if (B.debug("[session:request] received", {
3459
+ let t = q(), n = e.payload, r = t.getPendingAction(), i = r ?? (e.from === "local" && e.type === "REJECT" && ot(n?.action) ? n.action : null);
3460
+ if (V.debug("[session:request] received", {
3341
3461
  type: e.type,
3342
3462
  from: e.from,
3343
3463
  action: i,
@@ -3357,13 +3477,13 @@ var Qe = class {
3357
3477
  }
3358
3478
  if (e.from === "local") {
3359
3479
  if (!r && e.type === "REJECT") {
3360
- q({
3480
+ Y({
3361
3481
  type: "REJECT",
3362
3482
  payload: {
3363
3483
  action: i,
3364
3484
  reason: n?.reason ?? "rejected"
3365
3485
  }
3366
- }), B.debug("[session:request] local auto rejected", {
3486
+ }), V.debug("[session:request] local auto rejected", {
3367
3487
  action: i,
3368
3488
  reason: n?.reason
3369
3489
  });
@@ -3374,23 +3494,23 @@ var Qe = class {
3374
3494
  console.warn("[Request] Cannot APPROVE from current state");
3375
3495
  return;
3376
3496
  }
3377
- t.dispatchApprove(), i === "undo" ? t.applyUndo(t.getPendingUndoCount() ?? 1) : i === "restart" && t.resetGame(), q({
3497
+ t.dispatchApprove(), st(i), Y({
3378
3498
  type: "APPROVE",
3379
3499
  payload: { action: i }
3380
- }), t.clearPendingStates(), B.debug("[session:request] local approved", { action: i });
3500
+ }), t.clearPendingStates(), V.debug("[session:request] local approved", { action: i });
3381
3501
  return;
3382
3502
  }
3383
3503
  if (!t.canAction("local", "REJECT")) {
3384
3504
  console.warn("[Request] Cannot REJECT from current state");
3385
3505
  return;
3386
3506
  }
3387
- t.dispatchReject(), q({
3507
+ t.dispatchReject(), Y({
3388
3508
  type: "REJECT",
3389
3509
  payload: {
3390
3510
  action: i,
3391
3511
  reason: n?.reason ?? "rejected"
3392
3512
  }
3393
- }), t.clearPendingStates(), B.debug("[session:request] local rejected", { action: i });
3513
+ }), t.clearPendingStates(), V.debug("[session:request] local rejected", { action: i });
3394
3514
  return;
3395
3515
  }
3396
3516
  if (e.type === "APPROVE") {
@@ -3398,58 +3518,64 @@ var Qe = class {
3398
3518
  console.warn("[Request] Cannot APPROVE from current state (remote approved)");
3399
3519
  return;
3400
3520
  }
3401
- t.dispatchApprove(), i === "undo" ? t.applyUndo(t.getPendingUndoCount() ?? 1) : i === "restart" && t.resetGame(), t.clearPendingStates(), B.debug("[session:request] remote approved", { action: i });
3521
+ t.dispatchApprove(), st(i), t.clearPendingStates(), V.debug("[session:request] remote approved", { action: i });
3402
3522
  return;
3403
3523
  }
3404
3524
  if (!t.canAction("local", "REJECT")) {
3405
3525
  console.warn("[Request] Cannot REJECT from current state (remote rejected)"), t.clearPendingStates();
3406
3526
  return;
3407
3527
  }
3408
- t.dispatchReject(), t.clearPendingStates(), B.debug("[session:request] remote rejected", { action: i });
3409
- }, dt = (e) => e === "local" ? "remote" : "local", ft = () => {
3410
- let e = G();
3528
+ t.dispatchReject(), t.clearPendingStates(), V.debug("[session:request] remote rejected", { action: i });
3529
+ }, lt = (e) => e === "local" ? "remote" : "local", ut = () => {
3530
+ let e = q();
3411
3531
  return e.getResumeTurn() ?? (e.getState("local") === "turn" ? "local" : "remote");
3412
- }, pt = () => {
3413
- let e = G(), t = ft();
3532
+ }, dt = () => {
3533
+ let e = q(), t = ut();
3414
3534
  return {
3415
3535
  history: e.getHistory(),
3416
3536
  lastStart: e.getLastStart(),
3417
3537
  turn: t,
3418
- resumeTurn: e.getResumeTurn()
3538
+ resumeTurn: e.getResumeTurn(),
3539
+ outcome: e.getOutcome()
3419
3540
  };
3420
- }, mt = () => {
3421
- let e = G();
3541
+ }, ft = () => {
3542
+ let e = q();
3422
3543
  return e.getState("local") === "syncing" || e.getState("remote") === "syncing" || e.getState("remote") === "offline" || e.getResumeTurn() !== null;
3423
- }, ht = () => {
3424
- let e = G();
3544
+ }, pt = () => {
3545
+ let e = q();
3425
3546
  if (e.getState("local") !== "syncing") {
3426
- if (!e.canAction("local", "SYNC")) return B.debug("[session:sync] local cannot enter sync", { local: e.getState("local") }), !1;
3547
+ if (!e.canAction("local", "SYNC")) return V.debug("[session:sync] local cannot enter sync", { local: e.getState("local") }), !1;
3427
3548
  e.dispatch("local", "SYNC", "syncing");
3428
3549
  }
3429
3550
  if (e.getState("remote") === "offline") {
3430
- if (!e.canAction("remote", "ONLINE")) return B.debug("[session:sync] offline remote cannot enter sync", { remote: e.getState("remote") }), !1;
3551
+ if (!e.canAction("remote", "ONLINE")) return V.debug("[session:sync] offline remote cannot enter sync", { remote: e.getState("remote") }), !1;
3431
3552
  e.dispatch("remote", "ONLINE", "syncing");
3432
3553
  } else if (e.getState("remote") !== "syncing") {
3433
- if (!e.canAction("remote", "SYNC")) return B.debug("[session:sync] remote cannot enter sync", { remote: e.getState("remote") }), !1;
3554
+ if (!e.canAction("remote", "SYNC")) return V.debug("[session:sync] remote cannot enter sync", { remote: e.getState("remote") }), !1;
3434
3555
  e.dispatch("remote", "SYNC", "syncing");
3435
3556
  }
3436
3557
  return e.getState("local") === "syncing" && e.getState("remote") === "syncing";
3437
- }, J = (e, t) => {
3438
- let n = G(), r = t ? dt : (e) => e;
3558
+ }, X = (e, t) => {
3559
+ let n = q(), r = t ? lt : (e) => e;
3439
3560
  e.history && e.history.length > 0 ? n.replaceHistory(e.history.map((e) => ({
3440
3561
  ...e,
3441
3562
  player: r(e.player)
3442
3563
  }))) : n.clearHistory(), e.lastStart ? n.setLastStart(r(e.lastStart)) : n.setLastStart(null);
3443
- let i = e.resumeTurn ? r(e.resumeTurn) : e.turn ? r(e.turn) : ft();
3444
- B.debug("[session:sync] state restored", {
3564
+ let i = e.outcome ? e.outcome.kind === "win" ? {
3565
+ ...e.outcome,
3566
+ winner: r(e.outcome.winner)
3567
+ } : e.outcome : null;
3568
+ n.setOutcome(i);
3569
+ let a = e.resumeTurn ? r(e.resumeTurn) : e.turn ? r(e.turn) : ut();
3570
+ V.debug("[session:sync] state restored", {
3445
3571
  historyLength: n.getHistory().length,
3446
3572
  lastStart: n.getLastStart(),
3447
- nextTurnPlayer: i,
3573
+ nextTurnPlayer: a,
3448
3574
  mapped: t
3449
- }), ht() && n.dispatchSyncComplete(i);
3450
- }, gt = (e) => {
3451
- let t = G();
3452
- if (B.debug("[session:sync] received", {
3575
+ }), pt() && (i ? (n.dispatch("local", "GAME_OVER"), n.dispatch("remote", "GAME_OVER")) : n.dispatchSyncComplete(a));
3576
+ }, mt = (e) => {
3577
+ let t = q();
3578
+ if (V.debug("[session:sync] received", {
3453
3579
  type: e.type,
3454
3580
  from: e.from,
3455
3581
  payload: e.payload,
@@ -3463,37 +3589,37 @@ var Qe = class {
3463
3589
  console.warn("[Sync] Cannot SYNC from current state");
3464
3590
  return;
3465
3591
  }
3466
- t.getState("local") !== "syncing" && t.dispatch("local", "SYNC", "syncing"), t.getState("remote") !== "syncing" && t.dispatch("remote", "SYNC", "syncing"), q({
3592
+ t.getState("local") !== "syncing" && t.dispatch("local", "SYNC", "syncing"), t.getState("remote") !== "syncing" && t.dispatch("remote", "SYNC", "syncing"), Y({
3467
3593
  type: "SYNC_REQUEST",
3468
3594
  from: "",
3469
3595
  payload: e.payload
3470
- }), B.debug("[session:sync] request sent");
3596
+ }), V.debug("[session:sync] request sent");
3471
3597
  return;
3472
3598
  }
3473
- let n = pt();
3474
- q({
3599
+ let n = dt();
3600
+ Y({
3475
3601
  type: "SYNC_STATE",
3476
3602
  from: "",
3477
3603
  payload: n
3478
- }), B.debug("[session:sync] state sent", n), mt() && J(n, !1);
3604
+ }), V.debug("[session:sync] state sent", n), ft() && X(n, !1);
3479
3605
  return;
3480
3606
  }
3481
3607
  if (e.type === "SYNC_STATE") {
3482
3608
  if (e.from === "local") {
3483
- let e = pt();
3484
- q({
3609
+ let e = dt();
3610
+ Y({
3485
3611
  type: "SYNC_STATE",
3486
3612
  from: "",
3487
3613
  payload: e
3488
- }), B.debug("[session:sync] state pushed", e), mt() && J(e, !1);
3614
+ }), V.debug("[session:sync] state pushed", e), ft() && X(e, !1);
3489
3615
  return;
3490
3616
  }
3491
- J(e.payload || {}, !0);
3617
+ X(e.payload || {}, !0);
3492
3618
  }
3493
- }, _t = (e) => {
3619
+ }, ht = (e) => {
3494
3620
  if (e.type !== "UNDO") return;
3495
- let t = G(), n = K();
3496
- if (B.debug("[session:undo] received", {
3621
+ let t = q(), n = J();
3622
+ if (V.debug("[session:undo] received", {
3497
3623
  from: e.from,
3498
3624
  local: t.getState("local"),
3499
3625
  remote: t.getState("remote"),
@@ -3510,10 +3636,10 @@ var Qe = class {
3510
3636
  console.warn("[Undo] Not enough history to undo", { count: n });
3511
3637
  return;
3512
3638
  }
3513
- t.initializeUndoRequest(n, r), t.dispatch("local", "UNDO"), t.dispatch("remote", "REMOTE_UNDO"), q({
3639
+ t.initializeUndoRequest(n, r), t.dispatch("local", "UNDO"), t.dispatch("remote", "REMOTE_UNDO"), Y({
3514
3640
  type: "UNDO",
3515
3641
  payload: { count: n }
3516
- }), B.debug("[session:undo] local requested", { undoCount: n });
3642
+ }), V.debug("[session:undo] local requested", { undoCount: n });
3517
3643
  return;
3518
3644
  }
3519
3645
  if (t.hasPendingAction()) {
@@ -3546,14 +3672,14 @@ var Qe = class {
3546
3672
  return;
3547
3673
  }
3548
3674
  let a = t.getState("local") === "turn" ? "local" : "remote";
3549
- t.initializeUndoRequest(i, a), t.dispatch("local", "REMOTE_UNDO"), t.dispatch("remote", "UNDO"), B.debug("[session:undo] remote requested", {
3675
+ t.initializeUndoRequest(i, a), t.dispatch("local", "REMOTE_UNDO"), t.dispatch("remote", "UNDO"), V.debug("[session:undo] remote requested", {
3550
3676
  count: i,
3551
3677
  resumePlayer: a
3552
3678
  });
3553
- }, vt = (e) => {
3679
+ }, gt = (e) => {
3554
3680
  if (e.type !== "RESTART") return;
3555
- let t = G(), n = K();
3556
- if (B.debug("[session:restart] received", {
3681
+ let t = q(), n = J();
3682
+ if (V.debug("[session:restart] received", {
3557
3683
  from: e.from,
3558
3684
  local: t.getState("local"),
3559
3685
  remote: t.getState("remote"),
@@ -3566,7 +3692,7 @@ var Qe = class {
3566
3692
  return;
3567
3693
  }
3568
3694
  let e = t.getState("local") === "turn" ? "local" : "remote";
3569
- t.initializeRestartRequest(e), t.dispatch("local", "RESTART"), t.dispatch("remote", "REMOTE_RESTART"), q({ type: "RESTART" }), B.debug("[session:restart] local requested", { resumePlayer: e });
3695
+ t.initializeRestartRequest(e), t.dispatch("local", "RESTART"), t.dispatch("remote", "REMOTE_RESTART"), Y({ type: "RESTART" }), V.debug("[session:restart] local requested", { resumePlayer: e });
3570
3696
  return;
3571
3697
  }
3572
3698
  if (t.hasPendingAction()) {
@@ -3584,11 +3710,11 @@ var Qe = class {
3584
3710
  return;
3585
3711
  }
3586
3712
  let r = t.getState("local") === "turn" ? "local" : "remote";
3587
- t.initializeRestartRequest(r), t.dispatch("local", "REMOTE_RESTART"), t.dispatch("remote", "RESTART"), B.debug("[session:restart] remote requested", { resumePlayer: r });
3588
- }, yt = (e) => {
3713
+ t.initializeRestartRequest(r), t.dispatch("local", "REMOTE_RESTART"), t.dispatch("remote", "RESTART"), V.debug("[session:restart] remote requested", { resumePlayer: r });
3714
+ }, _t = (e) => {
3589
3715
  if (e.type !== "OFFLINE" && e.type !== "ONLINE") return;
3590
- let t = G(), n = K();
3591
- if (B.debug("[session:connection] received", {
3716
+ let t = q(), n = J();
3717
+ if (V.debug("[session:connection] received", {
3592
3718
  type: e.type,
3593
3719
  from: e.from,
3594
3720
  local: t.getState("local"),
@@ -3605,21 +3731,86 @@ var Qe = class {
3605
3731
  return;
3606
3732
  }
3607
3733
  let e = t.getResumeTurn() ?? (t.getState("local") === "turn" ? "local" : "remote");
3608
- t.setResumeTurn(e), t.dispatch("remote", "OFFLINE", "offline"), B.debug("[session:connection] remote offline", { currentTurn: e });
3734
+ t.setResumeTurn(e), t.dispatch("remote", "OFFLINE", "offline"), V.debug("[session:connection] remote offline", { currentTurn: e });
3609
3735
  return;
3610
3736
  }
3611
3737
  if (t.getState("remote") !== "offline") {
3612
- B.debug("[session:connection] ignored online while remote is not offline", { remote: t.getState("remote") });
3738
+ V.debug("[session:connection] ignored online while remote is not offline", { remote: t.getState("remote") });
3613
3739
  return;
3614
3740
  }
3615
3741
  if (!t.canAction("remote", "ONLINE")) {
3616
3742
  console.warn("[Offline] Cannot transition to ONLINE from current state");
3617
3743
  return;
3618
3744
  }
3619
- t.dispatch("remote", "ONLINE", "syncing"), n.emit("SYNC_STATE", void 0, "local"), B.debug("[session:connection] remote online, sync state pushed");
3620
- }, bt = (e) => {
3621
- e.register("READY", at), e.register("START", st), e.register("MOVE", ct), e.register("UNDO", _t), e.register("RESTART", vt), e.register("SYNC_REQUEST", gt), e.register("SYNC_STATE", gt), e.register("OFFLINE", yt), e.register("ONLINE", yt), e.register("APPROVE", ut), e.register("REJECT", ut);
3622
- }, xt = class {
3745
+ t.dispatch("remote", "ONLINE", "syncing"), n.emit("SYNC_STATE", void 0, "local"), V.debug("[session:connection] remote online, sync state pushed");
3746
+ }, vt = (e) => e === "draw", yt = () => q().getState("local") === "turn" ? "local" : "remote", bt = (e) => {
3747
+ if (e.type !== "REQUEST") return;
3748
+ let t = q(), n = J(), r = e.payload, i = r?.action;
3749
+ if (!vt(i)) {
3750
+ e.from === "remote" && Y({
3751
+ type: "REJECT",
3752
+ payload: {
3753
+ action: i,
3754
+ reason: "unknown_action"
3755
+ }
3756
+ });
3757
+ return;
3758
+ }
3759
+ if (t.getOutcome()) return;
3760
+ if (e.from === "local") {
3761
+ if (t.hasPendingAction() || !t.canAction("local", "REQUEST")) return;
3762
+ let e = yt();
3763
+ t.initializePendingRequest(i, e), t.dispatch("local", "REQUEST"), t.dispatch("remote", "REMOTE_REQUEST"), Y({
3764
+ type: "REQUEST",
3765
+ payload: {
3766
+ action: i,
3767
+ payload: r?.payload
3768
+ }
3769
+ }), V.debug("[session:request] local requested", { action: i });
3770
+ return;
3771
+ }
3772
+ if (t.hasPendingAction()) {
3773
+ n.emit("REJECT", {
3774
+ action: i,
3775
+ reason: "busy"
3776
+ }, "local");
3777
+ return;
3778
+ }
3779
+ if (!t.canAction("local", "REMOTE_REQUEST")) {
3780
+ n.emit("REJECT", {
3781
+ action: i,
3782
+ reason: "invalid_state"
3783
+ }, "local");
3784
+ return;
3785
+ }
3786
+ let a = yt();
3787
+ t.initializePendingRequest(i, a), t.dispatch("local", "REMOTE_REQUEST"), t.dispatch("remote", "REQUEST"), V.debug("[session:request] remote requested", { action: i });
3788
+ }, xt = (e) => {
3789
+ if (e.type !== "RESIGN") return;
3790
+ let t = q(), n = t.getOutcome();
3791
+ if (n?.kind === "win" && n.reason === "resignation") {
3792
+ t.setOutcome({
3793
+ kind: "draw",
3794
+ reason: "mutual_resignation"
3795
+ }), V.debug("[session:resign] simultaneous resignation resolved");
3796
+ return;
3797
+ }
3798
+ n || t.hasPendingAction() || (t.getState("local") === "turn" || t.getState("local") === "remote_turn") && (e.from === "local" ? (Y({ type: "RESIGN" }), t.completeGame({
3799
+ kind: "win",
3800
+ winner: "remote",
3801
+ reason: "resignation"
3802
+ })) : t.completeGame({
3803
+ kind: "win",
3804
+ winner: "local",
3805
+ reason: "resignation"
3806
+ }), V.debug("[session:resign] game completed", {
3807
+ from: e.from,
3808
+ outcome: t.getOutcome()
3809
+ }));
3810
+ }, St = (e) => {
3811
+ e.register("READY", nt), e.register("START", it), e.register("MOVE", at), e.register("UNDO", ht), e.register("RESTART", gt), e.register("REQUEST", bt), e.register("RESIGN", xt), e.register("SYNC_REQUEST", mt), e.register("SYNC_STATE", mt), e.register("OFFLINE", _t), e.register("ONLINE", _t), e.register("APPROVE", ct), e.register("REJECT", ct);
3812
+ }, Ct = class {
3813
+ bus;
3623
3814
  constructor(e) {
3624
3815
  this.bus = e;
3625
3816
  }
@@ -3638,18 +3829,30 @@ var Qe = class {
3638
3829
  restart() {
3639
3830
  this.bus.emit("RESTART");
3640
3831
  }
3832
+ request(e, t) {
3833
+ this.bus.emit("REQUEST", {
3834
+ action: e,
3835
+ payload: t
3836
+ });
3837
+ }
3838
+ offerDraw() {
3839
+ this.request("draw");
3840
+ }
3841
+ resign() {
3842
+ this.bus.emit("RESIGN");
3843
+ }
3641
3844
  approve() {
3642
3845
  this.bus.emit("APPROVE");
3643
3846
  }
3644
3847
  reject() {
3645
3848
  this.bus.emit("REJECT");
3646
3849
  }
3647
- }, St = (e, t) => {
3648
- let n = new Ge(), r = new $e(null, null), i = new Xe(), a = tt(e, n, null), o = new Qe(r, i, () => a.getIsConnected());
3649
- r.subscribeStateObserver(o), rt(r, n, a, t), bt(n);
3650
- let s = new xt(n);
3850
+ }, wt = (e, t) => {
3851
+ let n = new Ue(), r = new Xe(null, null), i = new Je(), a = Qe(e, n, null), o = new Ye(r, i, () => a.getIsConnected());
3852
+ r.subscribeStateObserver(o), et(r, n, a, t), St(n);
3853
+ let s = new Ct(n);
3651
3854
  return a.onConnectionChange((e) => {
3652
- i.notifyConnectionChange(e), i.notifyStateChange(Ze(r, e));
3855
+ i.notifyConnectionChange(e), i.notifyStateChange(W(r, e));
3653
3856
  }), {
3654
3857
  bus: n,
3655
3858
  state: r,
@@ -3658,7 +3861,7 @@ var Qe = class {
3658
3861
  actions: s,
3659
3862
  send: a.send.bind(a)
3660
3863
  };
3661
- }, Ct = "wss://signal.jiahengli.xyz", Y = (e) => ({
3864
+ }, Tt = "wss://signal.jiahengli.xyz", Et = (e) => ({
3662
3865
  screen: "pairing",
3663
3866
  theme: e?.theme || "light",
3664
3867
  gameTitle: e?.gameTitle || "P2P Lockstep",
@@ -3677,85 +3880,100 @@ var Qe = class {
3677
3880
  canStart: !1,
3678
3881
  canUndo: !1,
3679
3882
  canRestart: !1,
3883
+ canOfferDraw: !1,
3884
+ canResign: !1,
3885
+ allowDraw: e?.allowDraw ?? !1,
3886
+ allowResign: e?.allowResign ?? !1,
3680
3887
  started: !1,
3681
3888
  currentTurn: 1,
3682
3889
  turnOwner: null,
3683
3890
  localState: "idle",
3684
3891
  remoteState: "idle",
3685
3892
  pendingAction: null,
3893
+ outcome: null,
3686
3894
  historyLength: 0,
3687
3895
  lastStart: null,
3688
3896
  lastError: ""
3689
- }), wt = "p2p-lockstep-theme", X = (e) => e === "light" || e === "dark", Tt = () => {
3897
+ }), Dt = "p2p-lockstep-theme", Z = (e) => e === "light" || e === "dark", Ot = () => {
3690
3898
  try {
3691
- let e = window.localStorage.getItem(wt);
3692
- return X(e) ? e : null;
3899
+ let e = window.localStorage.getItem(Dt);
3900
+ return Z(e) ? e : null;
3693
3901
  } catch {
3694
3902
  return null;
3695
3903
  }
3696
- }, Et = (e) => {
3904
+ }, kt = (e) => {
3697
3905
  try {
3698
- window.localStorage.setItem(wt, e);
3906
+ window.localStorage.setItem(Dt, e);
3699
3907
  } catch {}
3700
- }, Z = {
3908
+ }, Q = {
3701
3909
  open: !1,
3702
3910
  title: "",
3703
3911
  description: "",
3704
3912
  confirmLabel: "Approve",
3705
3913
  cancelLabel: "Reject"
3706
- }, Dt = {
3914
+ }, At = {
3707
3915
  open: !1,
3708
3916
  message: ""
3709
- }, Ot = [
3917
+ }, jt = [
3710
3918
  "turn",
3711
3919
  "remote_turn",
3712
3920
  "approving",
3713
3921
  "waiting_approval",
3714
3922
  "syncing"
3715
- ], kt = class extends HTMLElement {
3923
+ ], Mt = class extends HTMLElement {
3716
3924
  static get observedAttributes() {
3717
3925
  return [
3718
3926
  "game-title",
3719
3927
  "session-id",
3720
3928
  "signal-url",
3721
- "theme"
3929
+ "theme",
3930
+ "allow-draw",
3931
+ "allow-resign"
3722
3932
  ];
3723
3933
  }
3724
3934
  #e = !1;
3725
- #t = Y();
3726
- #n = Z;
3727
- #r = Dt;
3728
- #i = null;
3935
+ #t = Et();
3936
+ #n = Q;
3937
+ #r = null;
3938
+ #i = At;
3729
3939
  #a = null;
3730
3940
  #o = null;
3731
3941
  #s = null;
3732
- #c = new He();
3733
- #l = null;
3942
+ #c = null;
3943
+ #l = new Be();
3734
3944
  #u = null;
3735
3945
  #d = null;
3736
3946
  #f = null;
3947
+ #p = null;
3948
+ #m = !1;
3949
+ #h = null;
3737
3950
  connectedCallback() {
3738
3951
  if (this.#e) return;
3739
3952
  this.#e = !0;
3740
- let e = X(this.getAttribute("theme")) ? this.getAttribute("theme") : "light", t = Tt() ?? e;
3741
- this.#t = Y({
3953
+ let e = Z(this.getAttribute("theme")) ? this.getAttribute("theme") : "light", t = Ot() ?? e;
3954
+ this.#t = Et({
3742
3955
  gameTitle: this.getAttribute("game-title") ?? void 0,
3743
3956
  sessionId: this.getAttribute("session-id") ?? void 0,
3744
3957
  signalUrl: this.getAttribute("signal-url") ?? void 0,
3745
- theme: t
3746
- }), this.getAttribute("theme") !== t && this.setAttribute("theme", t), this.render(), this.addEventListener("lockstep-register", this.#C), this.addEventListener("lockstep-connect", this.#w), this.addEventListener("lockstep-signal-change", this.#T), this.addEventListener("lockstep-target-change", this.#E), this.addEventListener("lockstep-share-detected", this.#D), this.addEventListener("lockstep-copy-share", this.#O), this.addEventListener("lockstep-theme-change", this.#k), this.addEventListener("lockstep-ready", this.#A), this.addEventListener("lockstep-start", this.#j), this.addEventListener("lockstep-undo", this.#M), this.addEventListener("lockstep-restart", this.#N), this.addEventListener("lockstep-dialog-confirm", this.#P), this.addEventListener("lockstep-dialog-cancel", this.#F), this.#p(), this.#m();
3958
+ theme: t,
3959
+ allowDraw: this.hasAttribute("allow-draw"),
3960
+ allowResign: this.hasAttribute("allow-resign")
3961
+ }), this.getAttribute("theme") !== t && this.setAttribute("theme", t), this.render(), this.addEventListener("lockstep-register", this.#O), this.addEventListener("lockstep-connect", this.#k), this.addEventListener("lockstep-signal-change", this.#A), this.addEventListener("lockstep-target-change", this.#j), this.addEventListener("lockstep-share-detected", this.#M), this.addEventListener("lockstep-copy-share", this.#N), this.addEventListener("lockstep-theme-change", this.#P), this.addEventListener("lockstep-ready", this.#F), this.addEventListener("lockstep-start", this.#I), this.addEventListener("lockstep-undo", this.#L), this.addEventListener("lockstep-restart", this.#R), this.addEventListener("lockstep-draw", this.#z), this.addEventListener("lockstep-resign", this.#B), this.addEventListener("lockstep-dialog-confirm", this.#V), this.addEventListener("lockstep-dialog-cancel", this.#H), document.addEventListener("visibilitychange", this.#U), window.addEventListener("pageshow", this.#W), this.#g(), this.#_();
3747
3962
  }
3748
3963
  disconnectedCallback() {
3749
- this.removeEventListener("lockstep-register", this.#C), this.removeEventListener("lockstep-connect", this.#w), this.removeEventListener("lockstep-signal-change", this.#T), this.removeEventListener("lockstep-target-change", this.#E), this.removeEventListener("lockstep-share-detected", this.#D), this.removeEventListener("lockstep-copy-share", this.#O), this.removeEventListener("lockstep-theme-change", this.#k), this.removeEventListener("lockstep-ready", this.#A), this.removeEventListener("lockstep-start", this.#j), this.removeEventListener("lockstep-undo", this.#M), this.removeEventListener("lockstep-restart", this.#N), this.removeEventListener("lockstep-dialog-confirm", this.#P), this.removeEventListener("lockstep-dialog-cancel", this.#F), this.#d &&= (window.clearTimeout(this.#d), null), this.#c.disconnect();
3964
+ this.removeEventListener("lockstep-register", this.#O), this.removeEventListener("lockstep-connect", this.#k), this.removeEventListener("lockstep-signal-change", this.#A), this.removeEventListener("lockstep-target-change", this.#j), this.removeEventListener("lockstep-share-detected", this.#M), this.removeEventListener("lockstep-copy-share", this.#N), this.removeEventListener("lockstep-theme-change", this.#P), this.removeEventListener("lockstep-ready", this.#F), this.removeEventListener("lockstep-start", this.#I), this.removeEventListener("lockstep-undo", this.#L), this.removeEventListener("lockstep-restart", this.#R), this.removeEventListener("lockstep-draw", this.#z), this.removeEventListener("lockstep-resign", this.#B), this.removeEventListener("lockstep-dialog-confirm", this.#V), this.removeEventListener("lockstep-dialog-cancel", this.#H), document.removeEventListener("visibilitychange", this.#U), window.removeEventListener("pageshow", this.#W), this.#f &&= (window.clearTimeout(this.#f), null), this.#l.disconnect();
3750
3965
  }
3751
3966
  attributeChangedCallback(e, t, n) {
3752
- this.#e && (e === "game-title" && n && this.#y({ gameTitle: n }), e === "session-id" && n && this.#y({ sessionId: n }), e === "signal-url" && n && this.#y({ signalUrl: n }), e === "theme" && X(n) && this.#y({ theme: n }));
3967
+ this.#e && (e === "game-title" && n && this.#w({ gameTitle: n }), e === "session-id" && n && this.#w({ sessionId: n }), e === "signal-url" && n && this.#w({ signalUrl: n }), e === "theme" && Z(n) && this.#w({ theme: n }), e === "allow-draw" && this.#w({ allowDraw: n !== null }), e === "allow-resign" && this.#w({ allowResign: n !== null }));
3753
3968
  }
3754
3969
  getRuntime() {
3755
- return this.#u;
3970
+ return this.#d;
3971
+ }
3972
+ resumeConnection() {
3973
+ this.#v();
3756
3974
  }
3757
3975
  getBoardHost() {
3758
- return this.#a?.getBoardHost() ?? null;
3976
+ return this.#o?.getBoardHost() ?? null;
3759
3977
  }
3760
3978
  render() {
3761
3979
  this.className = "block min-h-svh bg-[var(--lock-bg-deep)] text-[var(--lock-paper)]", this.innerHTML = `
@@ -3784,141 +4002,177 @@ var Qe = class {
3784
4002
  <p2p-lockstep-confirm-dialog></p2p-lockstep-confirm-dialog>
3785
4003
  <p2p-lockstep-toast-message></p2p-lockstep-toast-message>
3786
4004
  </div>
3787
- `, this.#i = this.querySelector("p2p-lockstep-pairing-page"), this.#a = this.querySelector("p2p-lockstep-game-page"), this.#o = this.querySelector("p2p-lockstep-confirm-dialog"), this.#s = this.querySelector("p2p-lockstep-toast-message"), this.#b();
3788
- }
3789
- #p() {
3790
- this.#l = St(this.#c, this.#t.sessionId), this.#u = {
3791
- setGamePlugin: (e) => this.#l?.state.setGamePlugin(e),
3792
- actions: { move: (e) => this.#l?.actions.move(e) },
4005
+ `, this.#a = this.querySelector("p2p-lockstep-pairing-page"), this.#o = this.querySelector("p2p-lockstep-game-page"), this.#s = this.querySelector("p2p-lockstep-confirm-dialog"), this.#c = this.querySelector("p2p-lockstep-toast-message"), this.#T();
4006
+ }
4007
+ #g() {
4008
+ this.#u = wt(this.#l, this.#t.sessionId), this.#d = {
4009
+ setGamePlugin: (e) => this.#u?.state.setGamePlugin(e),
4010
+ actions: {
4011
+ move: (e) => this.#u?.actions.move(e),
4012
+ offerDraw: () => this.#u?.actions.offerDraw(),
4013
+ resign: () => this.#u?.actions.resign()
4014
+ },
3793
4015
  observer: {
3794
- subscribe: (e) => this.#l?.observer.subscribe({
4016
+ subscribe: (e) => this.#u?.observer.subscribe({
3795
4017
  onStateChange: e.onStateChange,
3796
4018
  onConnectionChange: e.onConnectionChange,
3797
4019
  onError: e.onError,
3798
4020
  onGameEvent: e.onGameEvent ?? (() => {})
3799
4021
  }) ?? (() => {}),
3800
- getSnapshot: () => this.#l?.observer.getSnapshot() ?? null
4022
+ getSnapshot: () => this.#u?.observer.getSnapshot() ?? null
3801
4023
  }
3802
- }, this.#f = {
3803
- onStateChange: (e) => this.#_(e),
3804
- onConnectionChange: (e) => this.#v(e),
4024
+ }, this.#p = {
4025
+ onStateChange: (e) => this.#S(e),
4026
+ onConnectionChange: (e) => this.#C(e),
3805
4027
  onGameEvent: () => {},
3806
4028
  onError: (e) => {
3807
- this.#y({
4029
+ this.#w({
3808
4030
  lastError: e.message,
3809
4031
  connectionState: "error"
3810
- }), this.#x(e.message);
4032
+ }), this.#E(e.message);
3811
4033
  }
3812
- }, this.#l.observer.subscribe(this.#f), this.#y({
4034
+ }, this.#u.observer.subscribe(this.#p), this.#w({
3813
4035
  localState: "idle",
3814
4036
  remoteState: "idle",
3815
4037
  currentTurn: 1
3816
4038
  });
3817
4039
  }
3818
- async #m() {
3819
- let e = we();
3820
- e.signalUrl && this.#y({ signalUrl: e.signalUrl }), e.peerId && this.#y({ targetId: e.peerId }), this.#t.signalUrl && (await this.#h(this.#t.signalUrl, !0), e.peerId && await this.#g(e.peerId, !0));
3821
- }
3822
- async #h(e, t = !1) {
4040
+ async #_() {
4041
+ let e = Te();
4042
+ e.signalUrl && this.#w({ signalUrl: e.signalUrl }), e.peerId && this.#w({ targetId: e.peerId }), this.#t.signalUrl && (await this.#b(this.#t.signalUrl, !0), e.peerId && await this.#x(e.peerId, !0));
4043
+ }
4044
+ #v() {
4045
+ if (this.#h || !this.isConnected) return;
4046
+ let e = Te(), t = this.#l.getRemotePeerId() || this.#t.remotePeerId || e.peerId;
4047
+ this.#t.screen !== "game" || !this.#t.signalUrl || !t || (this.#h = this.#y(t).finally(() => {
4048
+ this.#h = null;
4049
+ }));
4050
+ }
4051
+ async #y(e) {
4052
+ this.#w({
4053
+ targetId: e,
4054
+ remotePeerId: e,
4055
+ connected: !1,
4056
+ connecting: !0,
4057
+ connectionState: "connecting",
4058
+ lastError: ""
4059
+ }), this.#E("Restoring peer connection."), await this.#b(this.#t.signalUrl, !0), this.#l.getLocalPeerId() && await this.#x(e, !0);
4060
+ }
4061
+ async #b(e, t = !1) {
3823
4062
  let n = e.trim();
3824
4063
  if (n) {
3825
- this.#y({
4064
+ this.#w({
3826
4065
  signalUrl: n,
3827
4066
  registering: !0,
3828
4067
  connectionState: "registering",
3829
4068
  lastError: ""
3830
4069
  });
3831
4070
  try {
3832
- let { peerId: e } = await this.#c.register(n);
3833
- this.#l?.net.setPeerIds({
4071
+ let { peerId: e } = await this.#l.register(n);
4072
+ this.#u?.net.setPeerIds({
3834
4073
  local: e,
3835
4074
  remote: this.#t.remotePeerId || null
3836
- }), this.#y({
4075
+ }), this.#w({
3837
4076
  peerId: e,
3838
4077
  registering: !1,
3839
4078
  connectionState: "registered"
3840
- }), t || this.#x("Peer registered. You can share the link now.");
4079
+ }), t || this.#E("Peer registered. You can share the link now.");
3841
4080
  } catch (e) {
3842
- this.#y({
4081
+ this.#w({
3843
4082
  registering: !1,
3844
4083
  connectionState: "error",
3845
4084
  lastError: e instanceof Error ? e.message : "Failed to register signaling session."
3846
- }), this.#x(this.#t.lastError || "Failed to register signaling session.");
4085
+ }), this.#E(this.#t.lastError || "Failed to register signaling session.");
3847
4086
  }
3848
4087
  }
3849
4088
  }
3850
- async #g(e, t = !1) {
4089
+ async #x(e, t = !1) {
3851
4090
  let n = e.trim();
3852
4091
  if (n) {
3853
- !this.#t.peerId && this.#t.signalUrl && await this.#h(this.#t.signalUrl, !0), this.#y({
4092
+ !this.#t.peerId && this.#t.signalUrl && await this.#b(this.#t.signalUrl, !0), this.#w({
3854
4093
  targetId: n,
3855
4094
  remotePeerId: n,
3856
4095
  connecting: !0,
3857
4096
  connectionState: "connecting",
3858
- screen: "game",
3859
4097
  lastError: ""
3860
- }), this.#l?.net.setPeerIds({
4098
+ }), this.#u?.net.setPeerIds({
3861
4099
  local: this.#t.peerId || null,
3862
4100
  remote: n
3863
4101
  });
3864
4102
  try {
3865
- await this.#c.connect(n), this.#y({ connecting: !1 }), t || this.#x("Connection request sent.");
4103
+ await this.#l.connect(n), this.#w({ connecting: !1 }), t || this.#E("Connection request sent.");
3866
4104
  } catch (e) {
3867
- this.#y({
4105
+ this.#w({
3868
4106
  connecting: !1,
3869
4107
  connectionState: "error",
3870
4108
  lastError: e instanceof Error ? e.message : "Failed to start peer connection."
3871
- }), this.#x(this.#t.lastError || "Failed to start peer connection.");
4109
+ }), this.#E(this.#t.lastError || "Failed to start peer connection.");
3872
4110
  }
3873
4111
  }
3874
4112
  }
3875
- #_(e) {
3876
- let t = e.connected, n = e.localState === "ready", r = e.remoteState === "ready", i = t && e.localState === "idle", a = t && e.localState === "could_start", o = t && (e.localState === "turn" || e.localState === "remote_turn") && !e.pendingAction && e.history.length > 0, s = t && (e.localState === "turn" || e.localState === "remote_turn") && !e.pendingAction, c = Ot.includes(e.localState) || e.localState === "offline" && e.lastStart !== null, l = e.localState === "turn" ? "me" : e.localState === "remote_turn" ? "peer" : null;
3877
- this.#y({
4113
+ #S(e) {
4114
+ let t = e.connected, n = e.localState === "ready", r = e.remoteState === "ready", i = !e.outcome && (e.localState === "turn" || e.localState === "remote_turn"), a = t && e.localState === "idle", o = t && e.localState === "could_start", s = t && i && !e.pendingAction && e.history.length > 0, c = t && i && !e.pendingAction, l = this.#t.allowDraw && t && i && !e.pendingAction, u = this.#t.allowResign && t && i && !e.pendingAction, d = jt.includes(e.localState) || e.localState === "offline" && e.lastStart !== null, f = e.localState === "turn" ? "me" : e.localState === "remote_turn" ? "peer" : null;
4115
+ if (this.#w({
3878
4116
  readySelf: n,
3879
4117
  readyPeer: r,
3880
4118
  connected: t,
3881
4119
  connectionState: t ? "connected" : this.#t.connectionState,
3882
- canReady: i,
3883
- canStart: a,
3884
- canUndo: o,
3885
- canRestart: s,
4120
+ canReady: a,
4121
+ canStart: o,
4122
+ canUndo: s,
4123
+ canRestart: c,
4124
+ canOfferDraw: l,
4125
+ canResign: u,
3886
4126
  screen: t ? "game" : this.#t.screen,
3887
- started: c,
4127
+ started: d,
3888
4128
  currentTurn: e.turn,
3889
- turnOwner: l,
4129
+ turnOwner: f,
3890
4130
  localState: e.localState,
3891
4131
  remoteState: e.remoteState,
3892
4132
  pendingAction: e.pendingAction,
4133
+ outcome: e.outcome,
3893
4134
  historyLength: e.history.length,
3894
4135
  lastStart: e.lastStart
3895
- }), e.localState === "approving" && e.pendingAction ? this.#n = {
3896
- open: !0,
3897
- title: e.pendingAction === "undo" ? "Undo request pending" : "Restart request pending",
3898
- description: e.pendingAction === "undo" ? "Your peer wants to roll the match back. Approve the undo or reject it and keep the current board." : "Your peer wants to restart the match. Approve to reset the board shell or reject to continue.",
3899
- confirmLabel: "Approve",
3900
- cancelLabel: "Reject"
3901
- } : this.#n.open && (this.#n = Z), this.#b();
3902
- }
3903
- #v(e) {
3904
- let t = this.#t.screen === "game", n = this.#c.peerState(), r = this.#c.getRemotePeerId() ?? this.#t.remotePeerId, i = this.#c.getLocalPeerId() ?? this.#t.peerId, a = e || n === "requesting" || t, o = n === "connected" ? "connected" : n === "requesting" ? "connecting" : t ? "offline" : i ? "registered" : "idle";
3905
- this.#y({
4136
+ }), e.localState === "approving" && e.pendingAction) {
4137
+ this.#r = "approval";
4138
+ let t = e.pendingAction === "undo" ? {
4139
+ title: "Undo request pending",
4140
+ description: "Your peer wants to roll the match back. Approve the undo or reject it and keep the current board."
4141
+ } : e.pendingAction === "restart" ? {
4142
+ title: "Restart request pending",
4143
+ description: "Your peer wants to restart the match. Approve to reset the board shell or reject to continue."
4144
+ } : {
4145
+ title: "Draw offer",
4146
+ description: "Your peer has offered a draw. Accept to end the match as a draw or reject to continue playing."
4147
+ };
4148
+ this.#n = {
4149
+ open: !0,
4150
+ ...t,
4151
+ confirmLabel: e.pendingAction === "draw" ? "Accept draw" : "Approve",
4152
+ cancelLabel: "Reject"
4153
+ };
4154
+ } else this.#r === "approval" && (this.#n = Q, this.#r = null);
4155
+ this.#T();
4156
+ }
4157
+ #C(e) {
4158
+ let t = this.#t.screen === "game", n = this.#l.peerState(), r = this.#l.getRemotePeerId() ?? this.#t.remotePeerId, i = this.#l.getLocalPeerId() ?? this.#t.peerId, a = e || t, o = n === "connected" ? "connected" : n === "requesting" ? "connecting" : t ? "offline" : i ? "registered" : "idle";
4159
+ this.#w({
3906
4160
  peerId: i,
3907
4161
  remotePeerId: r,
3908
4162
  connected: e,
3909
4163
  connecting: n === "requesting",
3910
4164
  connectionState: o,
3911
4165
  screen: a ? "game" : "pairing"
3912
- }), e ? this.#x("Peer connected. Game page is live.") : n === "requesting" && !t ? this.#x("Peer connection request received.") : t && i && this.#x("Peer disconnected. Waiting for reconnect.");
4166
+ }), e ? this.#E("Peer connected. Game page is live.") : n === "requesting" && !t ? this.#E("Connecting to peer.") : t && i && this.#E("Peer disconnected. Waiting for reconnect.");
3913
4167
  }
3914
- #y(e) {
4168
+ #w(e) {
3915
4169
  this.#t = {
3916
4170
  ...this.#t,
3917
4171
  ...e
3918
- }, this.#e && this.#b();
4172
+ }, this.#e && this.#T();
3919
4173
  }
3920
- #b() {
3921
- let e = this.#i, t = this.#a, n = this.#o, r = this.#s;
4174
+ #T() {
4175
+ let e = this.#a, t = this.#o, n = this.#s, r = this.#c;
3922
4176
  if (!e || !t || !n || !r) return;
3923
4177
  e.toggleAttribute("hidden", this.#t.screen !== "pairing"), t.toggleAttribute("hidden", this.#t.screen !== "game");
3924
4178
  let i = this.querySelector("[data-shell-connection-state]");
@@ -3945,88 +4199,115 @@ var Qe = class {
3945
4199
  canStart: this.#t.canStart,
3946
4200
  canUndo: this.#t.canUndo,
3947
4201
  canRestart: this.#t.canRestart,
4202
+ canOfferDraw: this.#t.canOfferDraw,
4203
+ canResign: this.#t.canResign,
4204
+ allowDraw: this.#t.allowDraw,
4205
+ allowResign: this.#t.allowResign,
3948
4206
  started: this.#t.started,
3949
4207
  currentTurn: this.#t.currentTurn,
3950
4208
  turnOwner: this.#t.turnOwner,
3951
4209
  localState: this.#t.localState,
3952
4210
  remoteState: this.#t.remoteState,
3953
4211
  pendingAction: this.#t.pendingAction,
4212
+ outcome: this.#t.outcome,
3954
4213
  sessionId: this.#t.sessionId,
3955
4214
  historyLength: this.#t.historyLength,
3956
4215
  lastStart: this.#t.lastStart,
3957
4216
  lastError: this.#t.lastError
3958
- }, n.state = this.#n, r.state = this.#r;
4217
+ }, n.state = this.#n, r.state = this.#i;
3959
4218
  }
3960
- #x(e) {
3961
- e && (this.#d && window.clearTimeout(this.#d), this.#r = {
4219
+ #E(e) {
4220
+ e && (this.#f && window.clearTimeout(this.#f), this.#i = {
3962
4221
  open: !0,
3963
4222
  message: e
3964
- }, this.#b(), this.#d = window.setTimeout(() => {
3965
- this.#r = Dt, this.#b(), this.#d = null;
4223
+ }, this.#T(), this.#f = window.setTimeout(() => {
4224
+ this.#i = At, this.#T(), this.#f = null;
3966
4225
  }, 2200));
3967
4226
  }
3968
- async #S(e) {
4227
+ async #D(e) {
3969
4228
  if (e) {
3970
4229
  try {
3971
4230
  if (navigator.clipboard?.writeText) {
3972
- await navigator.clipboard.writeText(e), this.#x("Copied to clipboard.");
4231
+ await navigator.clipboard.writeText(e), this.#E("Copied to clipboard.");
3973
4232
  return;
3974
4233
  }
3975
4234
  } catch {}
3976
4235
  window.prompt("Copy value", e);
3977
4236
  }
3978
4237
  }
3979
- #C = (e) => {
3980
- this.#h(e.detail.signalUrl);
4238
+ #O = (e) => {
4239
+ this.#b(e.detail.signalUrl);
3981
4240
  };
3982
- #w = (e) => {
3983
- this.#g(e.detail.targetId);
4241
+ #k = (e) => {
4242
+ this.#x(e.detail.targetId);
3984
4243
  };
3985
- #T = (e) => {
3986
- this.#y({ signalUrl: e.detail.signalUrl });
4244
+ #A = (e) => {
4245
+ this.#w({ signalUrl: e.detail.signalUrl });
3987
4246
  };
3988
- #E = (e) => {
3989
- this.#y({ targetId: e.detail.targetId });
4247
+ #j = (e) => {
4248
+ this.#w({ targetId: e.detail.targetId });
3990
4249
  };
3991
- #D = (e) => {
3992
- this.#y({
4250
+ #M = (e) => {
4251
+ this.#w({
3993
4252
  targetId: e.detail.peerId,
3994
4253
  signalUrl: e.detail.signalUrl || this.#t.signalUrl
3995
4254
  });
3996
4255
  };
3997
- #O = (e) => {
3998
- this.#S(e.detail.value);
4256
+ #N = (e) => {
4257
+ this.#D(e.detail.value);
3999
4258
  };
4000
- #k = (e) => {
4001
- if (!X(e.detail.theme) || e.detail.theme === this.#t.theme) return;
4259
+ #P = (e) => {
4260
+ if (!Z(e.detail.theme) || e.detail.theme === this.#t.theme) return;
4002
4261
  let t = e.detail.theme;
4003
- Et(t), this.setAttribute("theme", t), this.#x(`${t === "dark" ? "Night" : "Day"} mode enabled.`);
4262
+ kt(t), this.setAttribute("theme", t), this.#E(`${t === "dark" ? "Night" : "Day"} mode enabled.`);
4004
4263
  };
4005
- #A = () => {
4006
- this.#l?.actions.ready(), this.#x(this.#t.readySelf ? "Ready state cleared." : "Ready state sent.");
4264
+ #F = () => {
4265
+ this.#u?.actions.ready(), this.#E(this.#t.readySelf ? "Ready state cleared." : "Ready state sent.");
4007
4266
  };
4008
- #j = () => {
4009
- this.#l?.actions.start(), this.#x("Start request sent.");
4267
+ #I = () => {
4268
+ this.#u?.actions.start(), this.#E("Start request sent.");
4010
4269
  };
4011
- #M = () => {
4012
- this.#l?.actions.undo(), this.#x("Undo request sent.");
4270
+ #L = () => {
4271
+ this.#u?.actions.undo(), this.#E("Undo request sent.");
4013
4272
  };
4014
- #N = () => {
4015
- this.#l?.actions.restart(), this.#x("Restart request sent.");
4273
+ #R = () => {
4274
+ this.#u?.actions.restart(), this.#E("Restart request sent.");
4016
4275
  };
4017
- #P = () => {
4018
- this.#l?.actions.approve(), this.#n = Z, this.#b(), this.#x("Request approved.");
4276
+ #z = () => {
4277
+ this.#t.canOfferDraw && (this.#u?.actions.offerDraw(), this.#E("Draw offer sent."));
4019
4278
  };
4020
- #F = () => {
4021
- this.#l?.actions.reject(), this.#n = Z, this.#b(), this.#x("Request rejected.");
4279
+ #B = () => {
4280
+ this.#t.canResign && (this.#r = "resign", this.#n = {
4281
+ open: !0,
4282
+ title: "Resign this match?",
4283
+ description: "Resigning ends the current match immediately and awards the win to your peer.",
4284
+ confirmLabel: "Resign",
4285
+ cancelLabel: "Keep playing"
4286
+ }, this.#T());
4287
+ };
4288
+ #V = () => {
4289
+ this.#r === "resign" ? (this.#u?.actions.resign(), this.#E("You resigned the match.")) : (this.#u?.actions.approve(), this.#E("Request approved.")), this.#r = null, this.#n = Q, this.#T();
4290
+ };
4291
+ #H = () => {
4292
+ this.#r === "approval" && (this.#u?.actions.reject(), this.#E("Request rejected.")), this.#r = null, this.#n = Q, this.#T();
4293
+ };
4294
+ #U = () => {
4295
+ if (document.visibilityState === "hidden") {
4296
+ this.#m = !0;
4297
+ return;
4298
+ }
4299
+ this.#m && (this.#m = !1, this.#v());
4300
+ };
4301
+ #W = (e) => {
4302
+ e.persisted && this.#v();
4022
4303
  };
4023
- }, Q = (e, t) => {
4304
+ }, $ = (e, t) => {
4024
4305
  customElements.get(e) || customElements.define(e, t);
4025
- }, $ = () => {
4026
- Q("p2p-lockstep-share-panel", ve), Q("p2p-lockstep-status-panel", j), Q("p2p-lockstep-action-bar", d), Q("p2p-lockstep-confirm-dialog", g), Q("p2p-lockstep-toast-message", M), Q("p2p-lockstep-board-host", N), Q("p2p-lockstep-pairing-page", F), Q("p2p-lockstep-game-page", De), Q("p2p-lockstep-app", kt);
4306
+ }, Nt = () => {
4307
+ $("p2p-lockstep-share-panel", M), $("p2p-lockstep-status-panel", ve), $("p2p-lockstep-action-bar", d), $("p2p-lockstep-confirm-dialog", g), $("p2p-lockstep-toast-message", be), $("p2p-lockstep-board-host", xe), $("p2p-lockstep-pairing-page", F), $("p2p-lockstep-game-page", I), $("p2p-lockstep-app", Mt);
4027
4308
  };
4028
- $();
4309
+ Nt();
4029
4310
  //#endregion
4030
- export { Ct as DEFAULT_SIGNAL_URL, d as P2PLockstepActionBarElement, kt as P2PLockstepAppElement, N as P2PLockstepBoardHostElement, g as P2PLockstepConfirmDialogElement, De as P2PLockstepGamePageElement, F as P2PLockstepPairingPageElement, ve as P2PLockstepSharePanelElement, j as P2PLockstepStatusPanelElement, M as P2PLockstepToastMessageElement, $ as defineP2PLockstepUi };
4311
+ export { Tt as DEFAULT_SIGNAL_URL, d as P2PLockstepActionBarElement, Mt as P2PLockstepAppElement, xe as P2PLockstepBoardHostElement, g as P2PLockstepConfirmDialogElement, I as P2PLockstepGamePageElement, F as P2PLockstepPairingPageElement, M as P2PLockstepSharePanelElement, ve as P2PLockstepStatusPanelElement, be as P2PLockstepToastMessageElement, Nt as defineP2PLockstepUi };
4031
4312
 
4032
4313
  //# sourceMappingURL=index.js.map