/** * Word Search Maker: a form for the words and settings beside the puzzle they * make, which updates as the form changes and downloads as a PDF, and a Play * tab where the same puzzle is solved by tapping each word's two ends. * * The form owns the settings and hands its cells to `WordSearch`, which owns * generation, the answer view, shuffling and export. The word list is typed as * free text, one word or phrase per line (commas also separate), so pasting a * list from anywhere works. */ import { computed, Default, NAME, pattern, UI, type VNode, Writable, } from "commonfabric"; import { MAX_SIZE, MIN_SIZE, parseWordList, type WordSearch as Puzzle, } from "./generator.ts"; import { WordSearchPlay } from "./play.tsx"; import { WordSearch } from "./wordsearch.tsx"; interface WordSearchMakerInput { title?: Writable>; wordText?: Writable< string | Default<"apple\nbanana\ncherry\ngrape\nlemon\nmango\npeach"> >; rows?: Writable>; cols?: Writable>; diagonals?: Writable>; backwards?: Writable>; seed?: Writable>; } export interface WordSearchMakerOutput { [NAME]: string; [UI]: VNode; puzzle: Puzzle; } export default pattern( ({ title, wordText, rows, cols, diagonals, backwards, seed }) => { const words = computed(() => parseWordList(wordText.get())); const puzzle = WordSearch({ title, words, rows, cols, diagonals, backwards, seed, }); // The same puzzle, to play; its finds are each player's own. const play = WordSearchPlay({ title, puzzle: puzzle.puzzle }); // Which view of the puzzle this tab shows. const view = Writable.perSession.of("make"); // The size the grid has, which a typed-in value may have been brought to. const rowsLabel = computed(() => `Rows: ${puzzle.puzzle.rows}`); const colsLabel = computed(() => `Columns: ${puzzle.puzzle.cols}`); return { [NAME]: computed(() => title.get() || "Word Search"), [UI]: ( Diagonals Backwards
Make Play {puzzle} {play}
), puzzle: puzzle.puzzle, }; }, );