Have got win and failures working
This commit is contained in:
		
							parent
							
								
									d9fa154a01
								
							
						
					
					
						commit
						a8e42da6fd
					
				|  | @ -2,21 +2,32 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
|  | 	"bytes" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"flag" | 	"flag" | ||||||
| 	"log" | 	"log" | ||||||
|  | 	"math/rand/v2" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
| 	"sort" | 	"sort" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type wordList struct { | type wordList struct { | ||||||
| 	Words map[int][]string `json:"words"` | 	Words map[int][]string `json:"words"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type shufflePattern struct { | ||||||
|  | 	Index map[int][]int `json:"index"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func main() { | func main() { | ||||||
| 	dictFile := flag.String("dict", "", "dictionary of word to prep") | 	dictFile := flag.String("dict", "./dict/en_GB.dic", "dictionary of word to prep") | ||||||
|  | 	outDir := flag.String("out", "./site/assets/data", "output directory") | ||||||
| 	flag.Parse() | 	flag.Parse() | ||||||
| 
 | 
 | ||||||
|  | 	r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), uint64(time.Now().UnixNano()))) | ||||||
|  | 
 | ||||||
| 	words := wordList{ | 	words := wordList{ | ||||||
| 		Words: make(map[int][]string), | 		Words: make(map[int][]string), | ||||||
| 	} | 	} | ||||||
|  | @ -29,11 +40,42 @@ func main() { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, word := range words.Words { | 	for k, word := range words.Words { | ||||||
|  | 		log.Printf("Found %d words of length %v", len(word), k) | ||||||
| 		sort.Strings(word) | 		sort.Strings(word) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := json.NewEncoder(os.Stdout).Encode(words); err != nil { | 	var wordData bytes.Buffer | ||||||
|  | 	if err := json.NewEncoder(&wordData).Encode(words); err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if err := os.WriteFile(filepath.Join(*outDir, "words.json"), wordData.Bytes(), 0644); err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Generate a shuffle pattern
 | ||||||
|  | 	shp := shufflePattern{Index: make(map[int][]int)} | ||||||
|  | 	for k := range words.Words { | ||||||
|  | 		pattern := make([]int, len(words.Words[k])) | ||||||
|  | 		for i := range words.Words[k] { | ||||||
|  | 			pattern[i] = i | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for x := 4; x < r.IntN(8)+4; x++ { | ||||||
|  | 			r.Shuffle(len(pattern), func(i, j int) { | ||||||
|  | 				pattern[i], pattern[j] = pattern[j], pattern[i] | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// TODO: shuffle
 | ||||||
|  | 		shp.Index[k] = pattern | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var patternData bytes.Buffer | ||||||
|  | 	if err := json.NewEncoder(&patternData).Encode(shp); err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if err := os.WriteFile(filepath.Join(*outDir, "shuffle_pattern.json"), patternData.Bytes(), 0644); err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								site/assets/data/shuffle_pattern.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								site/assets/data/shuffle_pattern.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -32,6 +32,16 @@ export default class extends Controller { | ||||||
|       let key = ev.target.dataset["key"]; |       let key = ev.target.dataset["key"]; | ||||||
|       this.playfieldOutlet.tappedKey(key); |       this.playfieldOutlet.tappedKey(key); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   tapEnter(ev) { | ||||||
|  |     ev.preventDefault(); | ||||||
|  |     this.playfieldOutlet.enterGuess(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   tapBackspace(ev) { | ||||||
|  |     ev.preventDefault(); | ||||||
|  |     this.playfieldOutlet.tappedBackspace(); | ||||||
|  |   } | ||||||
|      |      | ||||||
|   resetKeyColors(ev) { |   resetKeyColors(ev) { | ||||||
|     for (let keyElement of this.keyTargets) { |     for (let keyElement of this.keyTargets) { | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								site/assets/scripts/controllers/overlay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								site/assets/scripts/controllers/overlay.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | import { Controller } from "https://unpkg.com/@hotwired/stimulus@v3.2.2/dist/stimulus.js" | ||||||
|  | 
 | ||||||
|  | export default class extends Controller { | ||||||
|  |     static targets = ["message"]; | ||||||
|  | 
 | ||||||
|  |     showMessage(msg) { | ||||||
|  |         this.messageTarget.innerText = msg; | ||||||
|  |         this.element.classList.add("show"); | ||||||
|  | 
 | ||||||
|  |         if (this._waitTimer) { | ||||||
|  |             clearTimeout(this._waitTimer); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this._waitTimer = setTimeout(() => { | ||||||
|  |             this.element.classList.remove("show"); | ||||||
|  |         }, 3000); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -5,7 +5,8 @@ import { WordSource } from "../models/words.js"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export default class extends Controller { | export default class extends Controller { | ||||||
|   static targets = ["row"]; |   static targets = ["row", "playfield", "topMessage", "nextPuzzleButton"]; | ||||||
|  |   static outlets = ["overlay"]; | ||||||
|    |    | ||||||
|   async connect() { |   async connect() { | ||||||
|     this._wordSource = new WordSource(); |     this._wordSource = new WordSource(); | ||||||
|  | @ -64,20 +65,30 @@ export default class extends Controller { | ||||||
|      |      | ||||||
|     switch (results.guessResult) { |     switch (results.guessResult) { | ||||||
|     case GUESS_RESULT.FOUL: |     case GUESS_RESULT.FOUL: | ||||||
|       console.log("not a word!"); |       this.overlayOutlet.showMessage("Not a valid word."); | ||||||
|  | 
 | ||||||
|       rowElem.replaceWith(this._buildPlayfieldRow(this._gameController.wordLength())); |       rowElem.replaceWith(this._buildPlayfieldRow(this._gameController.wordLength())); | ||||||
|       this._activeLetter = 0; |       this._activeLetter = 0; | ||||||
|       break; |       break; | ||||||
|     case GUESS_RESULT.MISS:     |     case GUESS_RESULT.MISS: | ||||||
|       console.log("try again!"); |  | ||||||
|        |  | ||||||
|       this._colorizeRow(rowElem, results); |       this._colorizeRow(rowElem, results); | ||||||
|  | 
 | ||||||
|       this._activeRowIndex += 1; |       this._activeRowIndex += 1; | ||||||
|       this._activeLetter = 0; |       if (this._activeRowIndex >= this._gameController.guesses()) { | ||||||
|  |         this.topMessageTarget.innerText = this._gameController.currentWord().toUpperCase(); | ||||||
|  |         this.nextPuzzleButtonTarget.classList.remove("hide"); | ||||||
|  |       } else { | ||||||
|  |         this._activeLetter = 0; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       break; |       break; | ||||||
|     case GUESS_RESULT.WIN: |     case GUESS_RESULT.WIN: | ||||||
|       console.log("CORRECT!"); |       this._colorizeRow(rowElem, results); | ||||||
|        | 
 | ||||||
|  |       this.topMessageTarget.innerText = "Hooray! You did it."; | ||||||
|  |       this.nextPuzzleButtonTarget.classList.remove("hide"); | ||||||
|  | 
 | ||||||
|  |       /* | ||||||
|       if (this._gameController.nextWord()) { |       if (this._gameController.nextWord()) { | ||||||
|         this._buildPlayfield(); |         this._buildPlayfield(); | ||||||
|       } else { |       } else { | ||||||
|  | @ -85,9 +96,19 @@ export default class extends Controller { | ||||||
|         this._activeRowIndex = -1; |         this._activeRowIndex = -1; | ||||||
|         this._colorizeRow(rowElem, results); |         this._colorizeRow(rowElem, results); | ||||||
|       } |       } | ||||||
|  |        */ | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async loadNextPuzzle(ev) { | ||||||
|  |     ev.preventDefault(); | ||||||
|  |     if (await this._gameController.nextWord()) { | ||||||
|  |       this._buildPlayfield(); | ||||||
|  |     } else { | ||||||
|  |       this.overlayOutlet.showMessage("No more words available."); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|    |    | ||||||
|   _buildPlayfield() { |   _buildPlayfield() { | ||||||
|     let rows = this._gameController.guesses(); |     let rows = this._gameController.guesses(); | ||||||
|  | @ -101,7 +122,10 @@ export default class extends Controller { | ||||||
|       newRows.push(this._buildPlayfieldRow(wordLength)); |       newRows.push(this._buildPlayfieldRow(wordLength)); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     this.element.replaceChildren.apply(this.element, newRows); |     this.playfieldTarget.replaceChildren.apply(this.playfieldTarget, newRows); | ||||||
|  | 
 | ||||||
|  |     this.topMessageTarget.innerHTML = " " | ||||||
|  |     this.nextPuzzleButtonTarget.classList.add("hide"); | ||||||
|      |      | ||||||
|     window.dispatchEvent(new CustomEvent("resetKeyColors")); |     window.dispatchEvent(new CustomEvent("resetKeyColors")); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -2,9 +2,11 @@ import { Application, Controller } from "https://unpkg.com/@hotwired/stimulus@v3 | ||||||
| 
 | 
 | ||||||
| import PlayfieldController from "./controllers/playfield.js"; | import PlayfieldController from "./controllers/playfield.js"; | ||||||
| import KeyboardController from "./controllers/keyboard.js"; | import KeyboardController from "./controllers/keyboard.js"; | ||||||
|  | import OverlayController from "./controllers/overlay.js"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| window.Stimulus = Application.start(); | window.Stimulus = Application.start(); | ||||||
| 
 | 
 | ||||||
| Stimulus.register("playfield", PlayfieldController); | Stimulus.register("playfield", PlayfieldController); | ||||||
| Stimulus.register("keyboard", KeyboardController); | Stimulus.register("keyboard", KeyboardController); | ||||||
|  | Stimulus.register("overlay", OverlayController); | ||||||
|  | @ -31,6 +31,11 @@ export class GameController { | ||||||
|     this._checkHasStarted(); |     this._checkHasStarted(); | ||||||
|     return this._guesses; |     return this._guesses; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   currentWord() { | ||||||
|  |     this._checkHasStarted(); | ||||||
|  |     return this._currentWord; | ||||||
|  |   } | ||||||
|    |    | ||||||
|   async nextWord() { |   async nextWord() { | ||||||
|     this._currentWord = await this._wordSource.getCurrentWord(); |     this._currentWord = await this._wordSource.getCurrentWord(); | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ function binSearch(list, word) { | ||||||
| export class WordSource { | export class WordSource { | ||||||
|     constructor() { |     constructor() { | ||||||
|         this._wordData = null; |         this._wordData = null; | ||||||
|  |         this._pattern = null; | ||||||
|         this._currentWord = null; |         this._currentWord = null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -38,7 +39,9 @@ export class WordSource { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let words = await this._fetchAllWordsIfNecessary(); |         let words = await this._fetchAllWordsIfNecessary(); | ||||||
|         this._currentWord = words.words["4"][7]; |         let idx = this._pattern.index["4"][7]; | ||||||
|  | 
 | ||||||
|  |         this._currentWord = words.words["4"][idx]; | ||||||
| 
 | 
 | ||||||
|         return this._currentWord; |         return this._currentWord; | ||||||
|     } |     } | ||||||
|  | @ -57,8 +60,8 @@ export class WordSource { | ||||||
|             return this._wordData; |             return this._wordData; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let res = await fetch("/assets/data/words.json"); |         this._wordData = await (await fetch("/assets/data/words.json")).json(); | ||||||
|         this._wordData = await res.json(); |         this._pattern = await (await fetch("/assets/data/shuffle_pattern.json")).json(); | ||||||
|         return this._wordData; |         return this._wordData; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,15 +1,90 @@ | ||||||
|  | :root { | ||||||
|  |     --color-no-letter-bg: #666; | ||||||
|  |     --color-no-letter-fg: #fff; | ||||||
|  | 
 | ||||||
|  |     --color-has-letter-bg: #dd4; | ||||||
|  |     --color-has-letter-fg: #000; | ||||||
|  | 
 | ||||||
|  |     --color-right-letter-bg: #4b4; | ||||||
|  |     --color-right-letter-fg: #fff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | body { | ||||||
|  |     height: 100vh; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | main { | ||||||
|  |     flex-grow: 1; | ||||||
|  |     flex-shrink: 1; | ||||||
|  | 
 | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | main > div:first-child { | ||||||
|  |     flex-grow: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.playfield { | ||||||
|  |     text-align: center; | ||||||
|  |     font-size: 1.5rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.playfield div[data-playfield-target="topMessage"] { | ||||||
|  |     font-weight: bold; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.playfield div[data-playfield-target="playfield"] { | ||||||
|  |     margin-block: 1rem; | ||||||
|  |     font-size: 3rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.keyboard > div { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: space-around; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.keyboard button { | ||||||
|  |     width: 9vw; | ||||||
|  |     border-radius: 2px; | ||||||
|  |     height: 4rem; | ||||||
|  |     text-transform: uppercase; | ||||||
|  | 
 | ||||||
|  |     background: #bbb; | ||||||
|  |     color: #000; | ||||||
|  |     border: solid 1px #444; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.keyboard > div:nth-child(2) { | ||||||
|  |     margin-inline: 2vw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.keyboard > div:nth-child(3) { | ||||||
|  |     margin-inline: 4vw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| button[data-keyboard-target="key"].right-pos { | button[data-keyboard-target="key"].right-pos { | ||||||
|     background: green; |     background: var(--color-right-letter-bg); | ||||||
|  |     color: var(--color-right-letter-fg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| button[data-keyboard-target="key"].right-char { | button[data-keyboard-target="key"].right-char { | ||||||
|     background: yellow; |     background: var(--color-has-letter-bg); | ||||||
|  |     color: var(--color-has-letter-fg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| button[data-keyboard-target="key"].miss { | button[data-keyboard-target="key"].miss { | ||||||
|     background: grey; |     background: var(--color-no-letter-bg); | ||||||
|  |     color: var(--color-no-letter-fg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | div.playfield div.row { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| div.playfield div.row span { | div.playfield div.row span { | ||||||
|     display: inline-block; |     display: inline-block; | ||||||
|  | @ -17,16 +92,54 @@ div.playfield div.row span { | ||||||
|      |      | ||||||
|     height: 1.1em; |     height: 1.1em; | ||||||
|     width: 1.1em; |     width: 1.1em; | ||||||
|  |     line-height: 1.1em; | ||||||
|  |     margin: 5px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| div.playfield div.row span.right-pos { | div.playfield div.row span.right-pos { | ||||||
|     background: green; |     background: var(--color-right-letter-bg); | ||||||
|  |     color: var(--color-right-letter-fg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| div.playfield div.row span.right-char { | div.playfield div.row span.right-char { | ||||||
|     background: yellow; |     background: var(--color-has-letter-bg); | ||||||
|  |     color: var(--color-has-letter-fg); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| div.playfield div.row span.miss { | div.playfield div.row span.miss { | ||||||
|     background: grey; |     background: var(--color-no-letter-bg); | ||||||
|  |     color: var(--color-no-letter-fg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.overlay { | ||||||
|  |     position: fixed; | ||||||
|  |     bottom: 25%; | ||||||
|  |     left: 10%; | ||||||
|  |     right: 10%; | ||||||
|  |     z-index: 10; | ||||||
|  | 
 | ||||||
|  |     display: none; | ||||||
|  |     justify-content: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.overlay.show { | ||||||
|  |     display: flex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.overlay-message { | ||||||
|  |     font-size: 2rem; | ||||||
|  |     line-height: 2.2rem; | ||||||
|  | 
 | ||||||
|  |     text-align: center; | ||||||
|  | 
 | ||||||
|  |     color: white; | ||||||
|  |     background-color: rgba(0, 0, 0, 70%); | ||||||
|  |     border-radius: 5px; | ||||||
|  | 
 | ||||||
|  |     padding-block: 12px; | ||||||
|  |     padding-inline: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hide { | ||||||
|  |     display: none; | ||||||
| } | } | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| <head> | <head> | ||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |     <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css"> | ||||||
|     <link href="/assets/styles/main.css" rel="stylesheet"> |     <link href="/assets/styles/main.css" rel="stylesheet"> | ||||||
|     <title>Wordle Clone</title> |     <title>Wordle Clone</title> | ||||||
| </head> | </head> | ||||||
|  | @ -12,11 +13,15 @@ | ||||||
|   </header> |   </header> | ||||||
| 
 | 
 | ||||||
|   <main> |   <main> | ||||||
|       <div data-controller="playfield" class="playfield">                 |       <div class="playfield" data-controller="playfield" | ||||||
|  |            data-playfield-overlay-outlet=".overlay"> | ||||||
| 
 | 
 | ||||||
|  |           <div data-playfield-target="topMessage"> </div> | ||||||
|  |           <div data-playfield-target="playfield"></div> | ||||||
|  | 
 | ||||||
|  |           <button data-playfield-target="nextPuzzleButton" class="hide" data-action="playfield#loadNextPuzzle">Next Puzzle</button> | ||||||
|       </div> |       </div> | ||||||
|        |       <div class="keyboard" data-controller="keyboard" | ||||||
|       <div data-controller="keyboard"  |  | ||||||
|            data-keyboard-playfield-outlet=".playfield" |            data-keyboard-playfield-outlet=".playfield" | ||||||
|            data-action=" |            data-action=" | ||||||
|               keydown@window->keyboard#onKeyPress |               keydown@window->keyboard#onKeyPress | ||||||
|  | @ -47,6 +52,7 @@ | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="l">l</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="l">l</button> | ||||||
|           </div> |           </div> | ||||||
|           <div> |           <div> | ||||||
|  |               <button data-keyboard-target="key" data-action="keyboard#tapBackspace">←</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="z">z</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="z">z</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="x">x</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="x">x</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="c">c</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="c">c</button> | ||||||
|  | @ -54,11 +60,10 @@ | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="b">b</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="b">b</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="n">n</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="n">n</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="m">m</button> |               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="m">m</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tapEnter">enter</button> |               <button data-keyboard-target="key" data-action="keyboard#tapEnter">↵</button> | ||||||
|               <button data-keyboard-target="key" data-action="keyboard#tapBackspace">back</button> |  | ||||||
|           </div> |           </div> | ||||||
|       </div> |       </div> | ||||||
|        | 
 | ||||||
|       <template id="row-template"> |       <template id="row-template"> | ||||||
|         <div class="row" data-playfield-target="row"> |         <div class="row" data-playfield-target="row"> | ||||||
|             <span></span> |             <span></span> | ||||||
|  | @ -70,6 +75,10 @@ | ||||||
|       </template> |       </template> | ||||||
|   </main> |   </main> | ||||||
| 
 | 
 | ||||||
|  |   <div class="overlay" data-controller="overlay"> | ||||||
|  |       <div class="overlay-message" data-overlay-target="message">I am the overlay</div> | ||||||
|  |   </div> | ||||||
|  | 
 | ||||||
|   <script src="/assets/scripts/main.js" type="module"></script> |   <script src="/assets/scripts/main.js" type="module"></script> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
		Loading…
	
		Reference in a new issue