diff --git a/multiplayer_crosswords/server/server_config.py b/multiplayer_crosswords/server/server_config.py
index f0b5daa..0b0458f 100644
--- a/multiplayer_crosswords/server/server_config.py
+++ b/multiplayer_crosswords/server/server_config.py
@@ -6,7 +6,7 @@ DEFAULT_WEBSOCKET_PORT = 8765
DEFAULT_MIN_GRID_SIZE = 10
DEFAULT_MAX_GRID_SIZE = 30
-DEFAULT_GRID_BLOCK_RATIO = 0.38
+DEFAULT_GRID_BLOCK_RATIO = 0.39
DEFAULT_MAX_SESSION_IDLE_TIME_SECONDS = 3600 * 48 # 2 days
diff --git a/multiplayer_crosswords/webui/grid.js b/multiplayer_crosswords/webui/grid.js
index 88372a4..0f70a46 100644
--- a/multiplayer_crosswords/webui/grid.js
+++ b/multiplayer_crosswords/webui/grid.js
@@ -17,6 +17,7 @@ export class CrosswordGrid extends LitElement {
_selected: { state: true },
_inputMode: { state: true }, // 'horizontal' or 'vertical'
_solvedCells: { state: true }, // tracks which cells are solved
+ _clueNumbers: { state: true }, // map of "row,col" -> { across: number, down: number }
};
// styles moved to webui/styles.css; render into light DOM so external CSS applies
@@ -29,6 +30,7 @@ export class CrosswordGrid extends LitElement {
this._selected = { r: 0, c: 0 };
this._inputMode = 'horizontal'; // default input mode
this._solvedCells = new Set(); // set of "r,c" strings for solved cells
+ this._clueNumbers = new Map(); // map of "row,col" -> { across: number, down: number }
this.sessionId = null; // Session ID for sending updates to server
}
@@ -119,7 +121,27 @@ export class CrosswordGrid extends LitElement {
}
}
- return html`
this._onCellClick(r, c)} data-r="${r}" data-c="${c}">${value}
`;
+ // Get clue numbers for this cell
+ const clueInfo = this._clueNumbers.get(cellKey);
+ let clueNumberDisplay = '';
+ if (clueInfo) {
+ if (clueInfo.across !== null && clueInfo.down !== null) {
+ // Both across and down clues: show "across/down" format
+ clueNumberDisplay = `${clueInfo.across}/${clueInfo.down}`;
+ } else if (clueInfo.across !== null) {
+ // Only across clue
+ clueNumberDisplay = String(clueInfo.across);
+ } else if (clueInfo.down !== null) {
+ // Only down clue
+ clueNumberDisplay = String(clueInfo.down);
+ }
+ }
+
+ const cellContent = clueNumberDisplay
+ ? html`${clueNumberDisplay}${value}`
+ : html`${value}`;
+
+ return html` this._onCellClick(r, c)} data-r="${r}" data-c="${c}">${cellContent}
`;
}
/**
@@ -465,6 +487,41 @@ export class CrosswordGrid extends LitElement {
console.log(`Letter update from server: [${row}, ${col}] = "${letter}" (solved: ${is_solved})`);
}
}
+
+ /**
+ * Populate clue numbers from server data
+ * @param {Object} cluePositionsAcross - dict of clue_number -> [col, row]
+ * @param {Object} cluePositionsDown - dict of clue_number -> [col, row]
+ */
+ populateClueNumbers(cluePositionsAcross = {}, cluePositionsDown = {}) {
+ this._clueNumbers.clear();
+
+ // Add across clues
+ for (const [clueNum, position] of Object.entries(cluePositionsAcross)) {
+ const [col, row] = position;
+ const cellKey = `${row},${col}`;
+
+ if (!this._clueNumbers.has(cellKey)) {
+ this._clueNumbers.set(cellKey, { across: null, down: null });
+ }
+
+ this._clueNumbers.get(cellKey).across = parseInt(clueNum);
+ }
+
+ // Add down clues
+ for (const [clueNum, position] of Object.entries(cluePositionsDown)) {
+ const [col, row] = position;
+ const cellKey = `${row},${col}`;
+
+ if (!this._clueNumbers.has(cellKey)) {
+ this._clueNumbers.set(cellKey, { across: null, down: null });
+ }
+
+ this._clueNumbers.get(cellKey).down = parseInt(clueNum);
+ }
+
+ this.requestUpdate();
+ }
}
customElements.define('crossword-grid', CrosswordGrid);
\ No newline at end of file
diff --git a/multiplayer_crosswords/webui/index.html b/multiplayer_crosswords/webui/index.html
index 0ee0cb6..30d4252 100644
--- a/multiplayer_crosswords/webui/index.html
+++ b/multiplayer_crosswords/webui/index.html
@@ -178,6 +178,9 @@
}
}
+ // Populate clue numbers for display
+ gridElement.populateClueNumbers(message.clue_positions_across, message.clue_positions_down);
+
gridElement.requestUpdate();
console.log(`Grid created: ${gridRows}x${gridCols} with ${wallPositions.length} walls`);
diff --git a/multiplayer_crosswords/webui/styles.css b/multiplayer_crosswords/webui/styles.css
index a05ad51..104dd7c 100644
--- a/multiplayer_crosswords/webui/styles.css
+++ b/multiplayer_crosswords/webui/styles.css
@@ -36,24 +36,35 @@ main {
justify-content: center;
align-items: flex-start;
padding: var(--page-padding);
- padding-bottom: var(--keyboard-space);
+ padding-bottom: 0; /* Remove keyboard space by default (desktop) */
min-height: calc(100vh - 0px);
box-sizing: border-box;
}
-.container { width: 100%; max-width: var(--max-container-width); }
+
+/* Add keyboard space on mobile devices */
+@media (max-width: 900px) {
+ main {
+ padding-bottom: var(--keyboard-space);
+ }
+}
+.container {
+ width: fit-content; /* Size exactly to content */
+ max-width: 100%; /* But don't exceed viewport */
+}
+
+#grid-container {
+ width: 100%;
+ overflow-x: auto;
+ overflow-y: auto;
+ display: flex;
+ justify-content: center; /* Center small grids */
+}
crossword-grid { display: block; margin: 0 auto; }
mobile-keyboard { display: block; }
h2, h3 { margin: 0.5rem 0; color: #f5f1ed; text-align: center; text-shadow: 0 2px 4px rgba(0,0,0,0.7); font-weight: 600; }
-crossword-grid {
- display: block;
- max-width: 100%;
- margin: 0 auto;
+crossword-grid { display: block; margin: 0 auto; }
- border-radius: 0px;
- background: var(--ink-dark);
- position: relative;
-}
.grid {
display: grid;
@@ -139,6 +150,26 @@ crossword-grid {
linear-gradient(135deg, #3a3530 0%, #2a2520 100%);
}
+/* Clue number styling - only in grid cells */
+.cell .clue-number {
+ position: absolute;
+ top: 2px;
+ left: 2px;
+ font-size: 0.55rem;
+ font-weight: 700;
+ line-height: 1;
+ color: #000000 !important;
+ pointer-events: none;
+}
+
+.cell-letter {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+}
+
.cell.mode-highlighted {
background:
repeating-linear-gradient(45deg, transparent, transparent 1px, rgba(100,180,200,.2) 1px, rgba(100,180,200,.2) 2px),
@@ -783,8 +814,21 @@ crossword-menu {
}
.game-content {
- display: flex;
- justify-content: center;
+ display: block;
+ overflow-y: auto;
+ overflow-x: auto;
+ max-height: calc(100vh - 200px); /* Leave space for header, clue area, and keyboard */
+ width: 100%;
+ padding: 1rem 0; /* Add some padding for breathing room */
+ text-align: center; /* Center inline-block children */
+}
+
+crossword-grid {
+ display: inline-block;
+ min-width: fit-content; /* Ensure grid takes its full needed width */
+ border-radius: 0px;
+ background: var(--ink-dark);
+ position: relative;
}
/* Clue Area */