commit 17e319990c49334da5898c9f473b865736ddb0ef Author: Luca Date: Tue May 20 19:10:24 2025 +0200 initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c760030 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.{css,html,js,json}] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d344ba6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.json diff --git a/app.js b/app.js new file mode 100644 index 0000000..fbb87a0 --- /dev/null +++ b/app.js @@ -0,0 +1,60 @@ +function randInt(max) { + return Math.floor(Math.random() * max); +} + +class App { + constructor() { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", this.init); + } else { + this.init(); + } + } + + async init() { + this.config = await (await fetch("/config.json")).json(); + + const gridConfig = this.config["grid"] ?? {}; + const grid = document.getElementById("grid"); + + const width = parseInt(gridConfig["width"] ?? 5); + if (width !== 5) { + grid.style.gridTemplateColumns = `repeat(${width}, 1fr)`; + } + + const height = parseInt(gridConfig["height"] ?? 5); + if (height !== 5) { + grid.style.gridTemplateRows = `repeat(${height}, 1fr)`; + } + + const phrases = this.config["phrases"] ?? []; + const probability = Math.min(phrases.length / Math.max(width * height - 1, 1), 1); + + const freeTile = this.config["freeTile"] ?? false; + for (let y = 0; y < height; ++y) { + for (let x = 0; x < width; ++x) { + const tile = document.createElement("div"); + tile.classList.add("tile"); + + if (x === 0) tile.classList.add("row-start"); + if (y === 0) tile.classList.add("col-start"); + + if ( + freeTile && freeTile.length + && x === Math.floor(width / 2) + && y === Math.floor(height / 2) + ) { + tile.innerText = freeTile[randInt(freeTile.length)]; + } else if (phrases && phrases.length && Math.random() < probability) { + const phrase = randInt(phrases.length); + tile.innerText = phrases[phrase]; + phrases.splice(phrase, 1); + } + + grid.append(tile); + } + } + } +} + +const app = new App(); diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..6809a01 --- /dev/null +++ b/config.example.json @@ -0,0 +1,11 @@ +{ + "freeTile": [ + "FREE" + ], + "grid": { + "height": 5, + "width": 5 + }, + "phrases": [ + ] +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..a3a5e02 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + GNOBI + + + + + +
+
+ + diff --git a/style.css b/style.css new file mode 100644 index 0000000..ed7581e --- /dev/null +++ b/style.css @@ -0,0 +1,76 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + align-items: center; + display: flex; + font-family: "Maven Pro", sans-serif; + justify-content: center; + min-height: 100vh; +} + +#grid { + aspect-ratio: 1 / 1; + display: grid; + grid-template-columns: repeat(5, 1fr); + grid-template-rows: repeat(5, 1fr); + width: 100%; +} + +.tile { + --border-width: 1px; + align-content: center; + border-color: #000; + border-style: solid; + border-width: 0 var(--border-width) var(--border-width) 0; + font-size: 12pt; + padding: 1em; + text-align: center; +} + +.tile.col-start { + border-top-width: var(--border-width); +} + +.tile.row-start { + border-left-width: var(--border-width); +} + +@media print { + #grid { + margin: 20mm; + } + + .tile { + --border-width: 1pt; + } +} + +@media print and (orientation: landscape) { + body { + height: 210mm; + width: 297mm; + } + + #grid { + height: calc(100% - 20mm * 2); + width: auto; + } +} + +@media print and (orientation: portrait) { + body { + height: 297mm; + width: 210mm; + } +} + +@media screen { + #grid { + margin: 2em; + max-width: 1000px; + } +}