@mideind/netskrafl-react 1.4.0 → 1.5.0

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.
@@ -929,14 +929,11 @@ div.netskrafl-container * {
929
929
  }
930
930
 
931
931
  .netskrafl-container div.board table.board {
932
- transform: scale(1);
933
- transform-origin: 0 0;
932
+ transform: translate(0px, 0px) scale(1);
933
+ transition: none;
934
+ transform-origin: top left;
934
935
  transform-box: view-box;
935
936
  touch-action: pan-x pan-y;
936
- /*
937
- !!! Scrolling into view doesn't seem to work with this transition enabled
938
- transition: transform .2s ease-in-out;
939
- */
940
937
  }
941
938
 
942
939
  .netskrafl-container table.board tr {
@@ -6281,7 +6278,7 @@ div.netskrafl-container input[type="checkbox"] {
6281
6278
  .netskrafl-container div.info {
6282
6279
  display: block;
6283
6280
  position: absolute;
6284
- bottom: 148px;
6281
+ bottom: 152px;
6285
6282
  top: auto;
6286
6283
  left: 28px;
6287
6284
  width: 50px;
@@ -6363,6 +6360,7 @@ div.netskrafl-container input[type="checkbox"] {
6363
6360
  width: auto;
6364
6361
  height: auto;
6365
6362
  margin: 0;
6363
+ overflow: visible; /* No scrollbars in fullscreen mode */
6366
6364
  }
6367
6365
  .netskrafl-container .board td {
6368
6366
  min-height: 30px;
@@ -7585,7 +7583,7 @@ div.netskrafl-container input[type="checkbox"] {
7585
7583
  .netskrafl-container .modal-dialog.gatadagsins-help {
7586
7584
  visibility: visible;
7587
7585
  position: fixed;
7588
- top: 50%;
7586
+ top: 52%;
7589
7587
  left: 50%;
7590
7588
  transform: translate(-50%, -50%);
7591
7589
  z-index: 10000;
@@ -7608,7 +7606,7 @@ div.netskrafl-container input[type="checkbox"] {
7608
7606
  padding: 15px;
7609
7607
  border-bottom: 1px solid #e0e0e0;
7610
7608
  display: flex;
7611
- justify-content: space-between;
7609
+ justify-content: flex-start;
7612
7610
  align-items: center;
7613
7611
  }
7614
7612
 
@@ -7618,22 +7616,37 @@ div.netskrafl-container input[type="checkbox"] {
7618
7616
  color: #333;
7619
7617
  }
7620
7618
 
7621
- .netskrafl-container .modal-dialog.gatadagsins-help .modal-header .close {
7622
- background: none;
7619
+ .netskrafl-container .modal-dialog.gatadagsins-help .modal-content {
7620
+ position: relative;
7621
+ }
7622
+
7623
+ .netskrafl-container .modal-dialog.gatadagsins-help .close {
7624
+ position: absolute;
7625
+ top: 10px;
7626
+ right: 10px;
7627
+ background-color: var(--malfridur-accent);
7623
7628
  border: none;
7624
- font-size: 28px;
7629
+ border-radius: 6px;
7625
7630
  cursor: pointer;
7626
7631
  padding: 0;
7627
- width: 30px;
7628
- height: 30px;
7632
+ width: 36px;
7633
+ height: 36px;
7629
7634
  display: flex;
7630
7635
  align-items: center;
7631
7636
  justify-content: center;
7632
- color: #999;
7637
+ color: white;
7638
+ z-index: 10;
7639
+ transition: all 0.2s ease;
7633
7640
  }
7634
7641
 
7635
- .netskrafl-container .modal-dialog.gatadagsins-help .modal-header .close:hover {
7636
- color: #333;
7642
+ .netskrafl-container .modal-dialog.gatadagsins-help .close .glyphicon {
7643
+ font-size: 18px;
7644
+ top: 0;
7645
+ }
7646
+
7647
+ .netskrafl-container .modal-dialog.gatadagsins-help .close:hover {
7648
+ background-color: var(--logo-accent);
7649
+ transform: scale(1.05);
7637
7650
  }
7638
7651
 
7639
7652
  .netskrafl-container .modal-dialog.gatadagsins-help .modal-body {
@@ -7718,9 +7731,9 @@ div.netskrafl-container input[type="checkbox"] {
7718
7731
  width: 100%;
7719
7732
  height: 100%;
7720
7733
  background-color: rgba(0, 0, 0, 0.5);
7721
- z-index: 9999;
7734
+ /* z-index: 9999; */
7722
7735
  overflow: hidden;
7723
- touch-action: none;
7736
+ /* touch-action: none; */
7724
7737
  }
7725
7738
 
7726
7739
  /* Larger screen adjustments */
@@ -7734,6 +7747,7 @@ div.netskrafl-container input[type="checkbox"] {
7734
7747
 
7735
7748
  .netskrafl-container .modal-dialog.gatadagsins-help .modal-header {
7736
7749
  padding: 20px;
7750
+ padding-right: 20px;
7737
7751
  }
7738
7752
 
7739
7753
  .netskrafl-container .modal-dialog.gatadagsins-help .modal-header h2 {
@@ -7834,6 +7848,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
7834
7848
  flex-direction: row;
7835
7849
  justify-content: space-evenly;
7836
7850
  align-items: center;
7851
+ margin-bottom: 20px;
7837
7852
  }
7838
7853
 
7839
7854
  /* Override rack positioning for Gáta Dagsins */
@@ -8697,7 +8712,11 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8697
8712
  /* ================= LEADERBOARD VIEW STYLES =============== */
8698
8713
 
8699
8714
  .netskrafl-container .leaderboard-view {
8715
+ display: flex;
8716
+ flex-direction: column;
8700
8717
  padding: 0px 10px;
8718
+ height: 100%;
8719
+ overflow: hidden;
8701
8720
  }
8702
8721
 
8703
8722
  .netskrafl-container .leaderboard-view.loading,
@@ -8716,6 +8735,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8716
8735
  display: flex;
8717
8736
  flex-direction: column;
8718
8737
  align-items: center;
8738
+ flex-shrink: 0;
8719
8739
  }
8720
8740
 
8721
8741
  .netskrafl-container .leaderboard-title {
@@ -8736,13 +8756,16 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8736
8756
  display: flex;
8737
8757
  flex-direction: column;
8738
8758
  gap: 8px;
8759
+ overflow-y: auto;
8760
+ height: 360px;
8761
+ overscroll-behavior-y: contain;
8739
8762
  }
8740
8763
 
8741
8764
  .netskrafl-container .leaderboard-entry {
8742
8765
  display: flex;
8743
8766
  align-items: center;
8744
- gap: 12px;
8745
- padding: 10px;
8767
+ gap: 8px;
8768
+ padding: 8px;
8746
8769
  background-color: #f8f8f8;
8747
8770
  border-radius: 6px;
8748
8771
  transition: all 0.3s ease;
@@ -8788,6 +8811,45 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8788
8811
  color: var(--malfridur-green);
8789
8812
  }
8790
8813
 
8814
+ .netskrafl-container .entry-star {
8815
+ display: inline-block;
8816
+ margin-right: 8px;
8817
+ color: var(--malfridur-secondary);
8818
+ line-height: 1;
8819
+ }
8820
+
8821
+ .netskrafl-container .entry-star .glyphicon {
8822
+ font-size: 13px;
8823
+ }
8824
+
8825
+ /* ================= HELP DIALOG BONUS SQUARES =============== */
8826
+
8827
+ .netskrafl-container .help-bonus-square {
8828
+ display: inline-block;
8829
+ width: 12px;
8830
+ height: 12px;
8831
+ border-radius: 2px;
8832
+ margin-left: 4px;
8833
+ margin-right: 4px;
8834
+ vertical-align: middle;
8835
+ }
8836
+
8837
+ .netskrafl-container .help-bonus-square.double-letter {
8838
+ background-color: var(--double-letter-color);
8839
+ }
8840
+
8841
+ .netskrafl-container .help-bonus-square.triple-letter {
8842
+ background-color: var(--triple-letter-color);
8843
+ }
8844
+
8845
+ .netskrafl-container .help-bonus-square.double-word {
8846
+ background-color: var(--double-word-color);
8847
+ }
8848
+
8849
+ .netskrafl-container .help-bonus-square.triple-word {
8850
+ background-color: var(--triple-word-color);
8851
+ }
8852
+
8791
8853
  /* ================= MOBILE STATS BUTTON =============== */
8792
8854
 
8793
8855
  .netskrafl-container .mobile-stats-button {
@@ -8825,7 +8887,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8825
8887
  .netskrafl-container .modal-dialog.stats-modal {
8826
8888
  visibility: visible;
8827
8889
  position: fixed;
8828
- top: 50%;
8890
+ top: 52%;
8829
8891
  left: 50%;
8830
8892
  transform: translate(-50%, -50%);
8831
8893
  z-index: 10000;
@@ -8884,7 +8946,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8884
8946
  .netskrafl-container .modal-dialog.stats-modal .modal-body {
8885
8947
  padding-top: 15px;
8886
8948
  padding-bottom: 15px;
8887
- overflow-y: auto;
8949
+ overflow-y: hidden;
8888
8950
  flex: 1;
8889
8951
  overscroll-behavior-y: contain;
8890
8952
  }
@@ -8892,6 +8954,11 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
8892
8954
  /* Desktop styles for Gáta Dagsins */
8893
8955
 
8894
8956
  @media all and (min-width: 1024px) {
8957
+
8958
+ .modal-backdrop-netskrafl {
8959
+ z-index: 9999;
8960
+ }
8961
+
8895
8962
  /* Show all thermometer moves on desktop */
8896
8963
  .netskrafl-container .thermometer-move-overlay:nth-child(n+2) {
8897
8964
  display: flex;
@@ -9018,6 +9085,11 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
9018
9085
  width: 100%;
9019
9086
  }
9020
9087
 
9088
+ .netskrafl-container .leaderboard-list {
9089
+ height: 514px; /* Space for 10 entries */
9090
+ overscroll-behavior-y: contain;
9091
+ }
9092
+
9021
9093
  /* Show thermometer on desktop */
9022
9094
  .netskrafl-container div.gatadagsins-thermometer-column {
9023
9095
  display: flex;
@@ -9034,7 +9106,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
9034
9106
  margin-top: 0;
9035
9107
  padding-top: 8px;
9036
9108
  padding-bottom: 24px;
9037
- overflow-x: hidden;
9109
+ overflow: hidden;
9038
9110
  }
9039
9111
 
9040
9112
  .netskrafl-container div.gatadagsins-rack-area {
@@ -9042,6 +9114,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
9042
9114
  /* Push left to compensate for row ids
9043
9115
  on the left side of the board */
9044
9116
  padding-left: 36px;
9117
+ margin-bottom: 0;
9045
9118
  }
9046
9119
 
9047
9120
  /* Show desktop score on desktop */
@@ -9086,4 +9159,8 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
9086
9159
  margin-right: 5px;
9087
9160
  }
9088
9161
 
9162
+ .netskrafl-container div.gatadagsins-container div.info {
9163
+ bottom: 42px;
9164
+ }
9165
+
9089
9166
  }
package/dist/esm/index.js CHANGED
@@ -31450,7 +31450,6 @@ const Movelist = (initialVnode) => {
31450
31450
  view,
31451
31451
  move,
31452
31452
  info: {
31453
- key: i.toString(),
31454
31453
  leftTotal: leftTotal, rightTotal: rightTotal,
31455
31454
  player: player, co: co, tiles: tiles, score: score
31456
31455
  }
@@ -31759,79 +31758,6 @@ const GameView = {
31759
31758
  For further information, see https://github.com/mideind/Netskrafl
31760
31759
 
31761
31760
  */
31762
- const vwReview = (view) => {
31763
- // A review of a finished game
31764
- var _a;
31765
- const model = view.model;
31766
- if (!model.game)
31767
- return undefined;
31768
- const game = model.game;
31769
- let moveIndex = (_a = model.reviewMove) !== null && _a !== void 0 ? _a : 0;
31770
- let bestMoves = model.bestMoves || [];
31771
- function vwRightColumn() {
31772
- // A container for the right-side header and area components
31773
- function vwRightHeading() {
31774
- // The right-side heading on the game screen
31775
- const fairplay = game.fairplay;
31776
- const player = game.player;
31777
- let sc0 = "";
31778
- let sc1 = "";
31779
- if (moveIndex) {
31780
- let s0 = 0;
31781
- let s1 = 0;
31782
- for (let i = 0; i < moveIndex; i++) {
31783
- // Add up the scores until and including this move
31784
- let m = game.moves[i];
31785
- if (i % 2 === 0)
31786
- s0 += m[1][2];
31787
- else
31788
- s1 += m[1][2];
31789
- }
31790
- sc0 = s0.toString();
31791
- sc1 = s1.toString();
31792
- }
31793
- return m(".heading", [
31794
- m(".logowrapper", m(".header-logo", m(m.route.Link, {
31795
- href: "/page",
31796
- class: "backlink"
31797
- }, m(NetskraflLogoOnly)))),
31798
- m(".playerwrapper", [
31799
- m(".leftplayer" + (player === 1 ? ".autoplayercolor" : ".humancolor"), [
31800
- m(".player", m(PlayerName, { view, side: "left" })),
31801
- m(".scorewrapper", m(".scoreleft", sc0)),
31802
- ]),
31803
- m(".rightplayer" + (player === 1 ? ".humancolor" : ".autoplayercolor"), [
31804
- m(".player", m(PlayerName, { view, side: "right" })),
31805
- m(".scorewrapper", m(".scoreright", sc1)),
31806
- ]),
31807
- m(".fairplay", { style: { visibility: fairplay ? "visible" : "hidden" } }, m("span.fairplay-btn.large", { title: ts("Skraflað án hjálpartækja") }))
31808
- ])
31809
- ]);
31810
- }
31811
- function vwRightArea() {
31812
- // A container for the list of best possible moves
31813
- return m(".right-area", vwBestMoves(view, moveIndex, bestMoves));
31814
- }
31815
- return m(".rightcol", [vwRightHeading(), vwRightArea()]);
31816
- }
31817
- let r = [];
31818
- if (game) {
31819
- // Create a list of major elements that we're showing
31820
- r.push(vwRightColumn());
31821
- r.push(m(BoardReview, { view, moveIndex }));
31822
- if (model.reviewMove !== null && moveIndex === 0) {
31823
- // Only show the stats overlay if moveIndex is 0
31824
- const n = vwStatsReview(view);
31825
- n && r.push(n);
31826
- }
31827
- }
31828
- return m("div", // Removing this div messes up Mithril
31829
- [
31830
- m(".game-container", r),
31831
- m(LeftLogo), // Button to go back to main screen
31832
- m(Info) // Help button
31833
- ]);
31834
- };
31835
31761
  const vwBestMoves = (view, moveIndex, bestMoves) => {
31836
31762
  // List of best moves, in a game review
31837
31763
  const model = view.model;
@@ -31886,7 +31812,7 @@ const vwBestMoves = (view, moveIndex, bestMoves) => {
31886
31812
  // and thus cannot be confused with the above abbreviations)
31887
31813
  wrdclass = "othermove";
31888
31814
  if (tiles == "--")
31889
- dispText = ts("Stafaleif: (engin)");
31815
+ dispText = ts("Stafaleif: engin");
31890
31816
  else
31891
31817
  dispText = [ts("Stafaleif: "), m("i.upper", tiles)];
31892
31818
  }
@@ -31903,8 +31829,8 @@ const vwBestMoves = (view, moveIndex, bestMoves) => {
31903
31829
  if (!game || !moveIndex || moveIndex > game.moves.length)
31904
31830
  return r;
31905
31831
  // Prepend a header that describes the move being reviewed
31906
- const m = game.moves[moveIndex - 1];
31907
- const [co, tiles, score] = m[1];
31832
+ const move = game.moves[moveIndex - 1];
31833
+ const [co, tiles, score] = move[1];
31908
31834
  r.push(bestHeader(co, tiles, score));
31909
31835
  const mlist = bestMoves;
31910
31836
  for (let i = 0; i < mlist.length; i++) {
@@ -31916,7 +31842,7 @@ const vwBestMoves = (view, moveIndex, bestMoves) => {
31916
31842
  }
31917
31843
  return r;
31918
31844
  }
31919
- return m(".movelist-container", [m(".movelist.bestmoves", bestMoveList())]);
31845
+ return m(".movelist-container", m(".movelist.bestmoves", bestMoveList()));
31920
31846
  };
31921
31847
  const vwBestMove = (view, moveIndex, bestMoveIndex, move, info) => {
31922
31848
  // Displays a move in a list of best available moves
@@ -32215,6 +32141,81 @@ const vwButtonsReview = (view, moveIndex) => {
32215
32141
  r.push(n);
32216
32142
  return r;
32217
32143
  };
32144
+ const Review = (initialVnode) => {
32145
+ // A review of a finished game
32146
+ const { view } = initialVnode.attrs;
32147
+ function vwRightColumn(game, moveIndex, bestMoves) {
32148
+ // A container for the right-side header and area components
32149
+ function vwRightHeading() {
32150
+ // The right-side heading on the game screen
32151
+ const fairplay = game.fairplay;
32152
+ const player = game.player;
32153
+ let sc0 = "";
32154
+ let sc1 = "";
32155
+ if (moveIndex) {
32156
+ let s0 = 0;
32157
+ let s1 = 0;
32158
+ for (let i = 0; i < moveIndex; i++) {
32159
+ // Add up the scores until and including this move
32160
+ let m = game.moves[i];
32161
+ if (i % 2 === 0)
32162
+ s0 += m[1][2];
32163
+ else
32164
+ s1 += m[1][2];
32165
+ }
32166
+ sc0 = s0.toString();
32167
+ sc1 = s1.toString();
32168
+ }
32169
+ return m(".heading", [
32170
+ m(".logowrapper", m(".header-logo", m(m.route.Link, {
32171
+ href: "/page",
32172
+ class: "backlink"
32173
+ }, m(NetskraflLogoOnly)))),
32174
+ m(".playerwrapper", [
32175
+ m(".leftplayer" + (player === 1 ? ".autoplayercolor" : ".humancolor"), [
32176
+ m(".player", m(PlayerName, { view, side: "left" })),
32177
+ m(".scorewrapper", m(".scoreleft", sc0)),
32178
+ ]),
32179
+ m(".rightplayer" + (player === 1 ? ".humancolor" : ".autoplayercolor"), [
32180
+ m(".player", m(PlayerName, { view, side: "right" })),
32181
+ m(".scorewrapper", m(".scoreright", sc1)),
32182
+ ]),
32183
+ m(".fairplay", { style: { visibility: fairplay ? "visible" : "hidden" } }, m("span.fairplay-btn.large", { title: ts("Skraflað án hjálpartækja") }))
32184
+ ])
32185
+ ]);
32186
+ }
32187
+ function vwRightArea() {
32188
+ // A container for the list of best possible moves
32189
+ return m(".right-area", vwBestMoves(view, moveIndex, bestMoves));
32190
+ }
32191
+ return m(".rightcol", [vwRightHeading(), vwRightArea()]);
32192
+ }
32193
+ return {
32194
+ view: () => {
32195
+ var _a;
32196
+ let r = [];
32197
+ const { model } = view;
32198
+ const { game } = model;
32199
+ if (game) {
32200
+ // Create a list of major elements that we're showing
32201
+ const moveIndex = (_a = model.reviewMove) !== null && _a !== void 0 ? _a : 0;
32202
+ const bestMoves = model.bestMoves || [];
32203
+ r.push(vwRightColumn(game, moveIndex, bestMoves));
32204
+ r.push(m(BoardReview, { view, moveIndex }));
32205
+ if (model.reviewMove !== null && moveIndex === 0) {
32206
+ // Only show the stats overlay if moveIndex is 0
32207
+ const n = vwStatsReview(view);
32208
+ n && r.push(n);
32209
+ }
32210
+ }
32211
+ return m("div", [
32212
+ m(".game-container", r),
32213
+ m(LeftLogo), // Button to go back to main screen
32214
+ m(Info) // Help button
32215
+ ]);
32216
+ }
32217
+ };
32218
+ };
32218
32219
  const BoardReview = {
32219
32220
  // The board area within a game review screen
32220
32221
  view: (vnode) => {
@@ -32294,8 +32295,7 @@ class View {
32294
32295
  views.push(m(GameView, { key: "game", view: this }));
32295
32296
  break;
32296
32297
  case "review":
32297
- const n = vwReview(this);
32298
- n && views.push(n);
32298
+ views.push(m(Review, { key: "review", view: this }));
32299
32299
  break;
32300
32300
  case "thanks":
32301
32301
  // Display a thank-you dialog on top of the normal main screen
@@ -35873,6 +35873,8 @@ class Actions {
35873
35873
  if (state && !state.uiFullscreen) {
35874
35874
  state.uiFullscreen = true;
35875
35875
  if (view) {
35876
+ // Reset zoom when switching to fullscreen (desktop always uses scale 1)
35877
+ view.resetScale();
35876
35878
  view.notifyMediaChange();
35877
35879
  }
35878
35880
  m.redraw();
@@ -36213,6 +36215,8 @@ class Actions {
36213
36215
  // Listen to user stats (if user is logged in)
36214
36216
  if (state === null || state === void 0 ? void 0 : state.userId) {
36215
36217
  attachFirebaseListener(`gatadagsins/users/${locale}/${state.userId}/stats`, (json, firstAttach) => this.onUserStatsUpdate(json, firstAttach));
36218
+ // Listen to personal best move (entire achievement object)
36219
+ attachFirebaseListener(basePath + `achievements/${state.userId}`, (json, firstAttach) => this.onPersonalBestScoreUpdate(json, firstAttach));
36216
36220
  }
36217
36221
  }
36218
36222
  detachListenerFromRiddle(date, locale) {
@@ -36225,6 +36229,7 @@ class Actions {
36225
36229
  detachFirebaseListener(basePath + "leaders");
36226
36230
  if (state === null || state === void 0 ? void 0 : state.userId) {
36227
36231
  detachFirebaseListener(`gatadagsins/users/${locale}/${state.userId}/stats`);
36232
+ detachFirebaseListener(basePath + `achievements/${state.userId}`);
36228
36233
  }
36229
36234
  }
36230
36235
  onRiddleGlobalScoreUpdate(json, firstAttach) {
@@ -36275,6 +36280,35 @@ class Actions {
36275
36280
  }
36276
36281
  m.redraw();
36277
36282
  }
36283
+ onPersonalBestScoreUpdate(json, firstAttach) {
36284
+ const { riddle } = this.model;
36285
+ if (!riddle || !json)
36286
+ return;
36287
+ // Extract the full move from Firebase
36288
+ const score = json.score || 0;
36289
+ const word = json.word || "";
36290
+ const coord = json.coord || "";
36291
+ const timestamp = json.timestamp || new Date().toISOString();
36292
+ // Only proceed if we didn't already have this score or better
36293
+ if (score <= riddle.personalBestScore || !word || !coord)
36294
+ return;
36295
+ riddle.personalBestScore = score;
36296
+ // Check if this move is already in playerMoves
36297
+ // (this is a safety precaution; normally the
36298
+ // move should not be there already)
36299
+ const moveExists = riddle.playerMoves.some(m => m.word === word && m.coord === coord && m.score === score);
36300
+ // If not already present, add it to playerMoves
36301
+ // This enables clicking on it to see it on the board
36302
+ if (!moveExists) {
36303
+ riddle.playerMoves.push({
36304
+ word,
36305
+ score,
36306
+ coord,
36307
+ timestamp
36308
+ });
36309
+ }
36310
+ m.redraw();
36311
+ }
36278
36312
  async fetchRiddle(date, locale) {
36279
36313
  // Create the game via model
36280
36314
  if (!this.model)
@@ -36632,7 +36666,6 @@ const MobileStatus = () => {
36632
36666
  return null;
36633
36667
  const { bestPossibleScore, globalBestScore, personalBestScore } = riddle;
36634
36668
  // Determine if player achieved best possible score
36635
- const achieved = bestMove !== undefined;
36636
36669
  const celebrate = bestMove && bestMove.word !== "";
36637
36670
  // Determine current leader score (may be this player or another)
36638
36671
  let leaderScore = 0;
@@ -36669,8 +36702,7 @@ const MobileStatus = () => {
36669
36702
  ]),
36670
36703
  // Best possible score
36671
36704
  m(".mobile-status-item.right.best-possible"
36672
- + (celebrate ? ".celebrate" : "")
36673
- + (achieved ? ".achieved" : ""), {
36705
+ + (celebrate ? ".celebrate" : ""), {
36674
36706
  onclick: () => celebrate && onMoveClick(bestMove.word, bestMove.coord)
36675
36707
  }, [
36676
36708
  // Wrapper for score and corona to position them together
@@ -36791,25 +36823,17 @@ const BestPossibleScore = () => {
36791
36823
  const { score, bestMove, onMoveClick } = vnode.attrs;
36792
36824
  // Determine the label based on achievement status
36793
36825
  let topLabel;
36794
- if (bestMove !== undefined) {
36795
- if (bestMove.word) {
36796
- // Current player achieved it - show their word
36797
- topLabel = removeBlankMarkers(bestMove.word);
36798
- }
36799
- else {
36800
- // Someone else achieved it - indicate this
36801
- topLabel = ts("Besta mögulega lögn\n(er þegar fundin)");
36802
- }
36826
+ if (bestMove !== undefined && bestMove.word) {
36827
+ // Current player achieved it - show their word
36828
+ topLabel = removeBlankMarkers(bestMove.word);
36803
36829
  }
36804
36830
  else {
36805
36831
  // Not achieved yet - show default label
36806
36832
  topLabel = ts("Besta mögulega lögn");
36807
36833
  }
36808
- const achieved = bestMove !== undefined;
36809
36834
  const celebrate = bestMove && bestMove.word !== "";
36810
36835
  return m(".thermometer-best-score"
36811
- + (celebrate ? ".celebrate" : "")
36812
- + (achieved ? ".achieved" : ""), m(".thermometer-best-score-container", {
36836
+ + (celebrate ? ".celebrate" : ""), m(".thermometer-best-score-container", {
36813
36837
  onclick: () => celebrate && onMoveClick(bestMove.word, bestMove.coord)
36814
36838
  }, [
36815
36839
  // Sun corona behind the circle when celebrating
@@ -37045,7 +37069,10 @@ const LeaderboardView = {
37045
37069
  m(".leaderboard-header", [
37046
37070
  m(".leaderboard-title", formatDate(date)),
37047
37071
  ]),
37048
- m(".leaderboard-list", leaderboard.map((entry, index) => {
37072
+ m(".leaderboard-list", {
37073
+ // Allow touch scrolling but prevent events from bubbling to backdrop
37074
+ ontouchmove: (e) => { e.stopPropagation(); }
37075
+ }, leaderboard.map((entry, index) => {
37049
37076
  const rank = index + 1;
37050
37077
  const isCurrentUser = entry.userId === currentUserId;
37051
37078
  const medal = getMedalIcon(rank);
@@ -37053,7 +37080,9 @@ const LeaderboardView = {
37053
37080
  m(".entry-rank", [
37054
37081
  medal ? m("span.medal", medal) : m("span.rank-number", rank.toString())
37055
37082
  ]),
37056
- m(".entry-name", isCurrentUser ? ts("Þú") : entry.displayName),
37083
+ m(".entry-name", isCurrentUser
37084
+ ? [m("span.entry-star", glyph("star")), ts("Þú")]
37085
+ : entry.displayName),
37057
37086
  m(".entry-score", entry.score.toString())
37058
37087
  ]);
37059
37088
  }))
@@ -37154,7 +37183,7 @@ const GataDagsinsRightSide = {
37154
37183
  view: (vnode) => {
37155
37184
  const { view, selectedMoves, bestMove, onStatsClick } = vnode.attrs;
37156
37185
  const { riddle } = view.model;
37157
- const handleMoveClick = (word, coord) => {
37186
+ const onMoveClick = (word, coord) => {
37158
37187
  if (riddle && word && coord) {
37159
37188
  // Recreate the word on the board
37160
37189
  riddle.recreateWordOnBoard(word, coord);
@@ -37166,15 +37195,15 @@ const GataDagsinsRightSide = {
37166
37195
  view,
37167
37196
  selectedMoves,
37168
37197
  bestMove,
37169
- onMoveClick: handleMoveClick,
37170
- onStatsClick
37198
+ onMoveClick,
37199
+ onStatsClick,
37171
37200
  })),
37172
37201
  // Desktop-only tabbed view (hidden on mobile, visible on desktop)
37173
37202
  m(".gatadagsins-thermometer-column", m(RightSideTabs, {
37174
37203
  view,
37175
37204
  selectedMoves,
37176
37205
  bestMove,
37177
- onMoveClick: handleMoveClick
37206
+ onMoveClick,
37178
37207
  })),
37179
37208
  ] : null);
37180
37209
  }
@@ -37205,14 +37234,13 @@ const GataDagsinsHelp = {
37205
37234
  ontouchmove: (e) => { e.preventDefault(); e.stopPropagation(); }
37206
37235
  }),
37207
37236
  m(".modal-dialog.gatadagsins-help", m(".modal-content", [
37208
- // Header with close button
37209
- m(".modal-header", [
37210
- m("h2", "Um Gátu dagsins"),
37211
- m("button.close", {
37212
- onclick: closeHelp,
37213
- "aria-label": "Loka"
37214
- }, m("span", { "aria-hidden": "true" }, "×"))
37215
- ]),
37237
+ // Close button (positioned absolutely in top-right corner)
37238
+ m("button.close", {
37239
+ onclick: closeHelp,
37240
+ "aria-label": "Loka"
37241
+ }, glyph("remove")),
37242
+ // Header
37243
+ m(".modal-header", m("h2", "Um Gátu dagsins")),
37216
37244
  // Body with help content
37217
37245
  m(".modal-body", [
37218
37246
  m("p", "Gáta dagsins er dagleg krossgátuþraut, svipuð skrafli, þar sem þú reynir að finna " +
@@ -37231,8 +37259,20 @@ const GataDagsinsHelp = {
37231
37259
  m("ul", [
37232
37260
  m("li", "Hver stafur gefur 1-10 stig eftir gildi hans"),
37233
37261
  m("li", "Orð sem nota allar 7 stafaflísarnar gefa 50 stiga bónus"),
37234
- m("li", "Sumir reitir á borðinu tvöfalda eða þrefalda stafagildið"),
37235
- m("li", "Sumir reitir tvöfalda eða þrefalda heildarorðagildið"),
37262
+ m("li", [
37263
+ "Sumir reitir á borðinu ",
37264
+ m("span.help-bonus-square.double-letter"),
37265
+ "tvöfalda eða ",
37266
+ m("span.help-bonus-square.triple-letter"),
37267
+ "þrefalda stafagildið"
37268
+ ]),
37269
+ m("li", [
37270
+ "Sumir reitir ",
37271
+ m("span.help-bonus-square.double-word"),
37272
+ "tvöfalda eða ",
37273
+ m("span.help-bonus-square.triple-word"),
37274
+ "þrefalda heildarorðagildið"
37275
+ ]),
37236
37276
  ]),
37237
37277
  m("h3", "Hitamælir"),
37238
37278
  m("p", "Hitamælirinn hægra megin (eða efst á farsímum) sýnir:"),