Files
wordsearch/app/templates/index.html
2026-05-04 09:45:17 +01:00

163 lines
5.6 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}Generate · Wordsmith{% endblock %}
{% block content %}
<section class="container">
<div class="page-head">
<div class="page-head__title">
<div class="eyebrow">№ 01 · Studio</div>
<h1>Generate a puzzle</h1>
</div>
<div class="page-head__meta">{{ themes|length }} theme{{ '' if themes|length == 1 else 's' }} on file</div>
</div>
<hr class="section-rule" />
{% if errors %}
<div class="notice">
<strong>Hold on</strong>
<ul>
{% for e in errors %}<li>{{ e }}</li>{% endfor %}
</ul>
</div>
{% endif %}
{% if not themes %}
<div class="panel">
<p>No themes yet. <a href="/themes/new">Create one</a> to get started.</p>
</div>
{% else %}
<form class="studio" method="post" action="/generate">
<div class="form-grid">
<label class="field">
<span class="field__label">Theme</span>
<select name="theme" id="theme" required>
{% for t in themes %}
<option value="{{ t.slug }}" data-words="{{ t.words|join(',') }}">{{ t.name }} ({{ t.words|length }} words)</option>
{% endfor %}
</select>
</label>
<div class="row">
<label class="field">
<span class="field__label">Grid</span>
<input type="number" name="grid_size" id="grid-size" value="12" min="5" max="25" required />
</label>
<label class="field">
<span class="field__label">Words</span>
<input type="number" name="word_count" id="word-count-input" value="10" min="1" max="30" required />
</label>
<label class="field">
<span class="field__label">Min len</span>
<input type="number" name="min_length" value="3" min="1" required />
</label>
<label class="field">
<span class="field__label">Max len</span>
<input type="number" name="max_length" value="12" min="1" required />
</label>
</div>
<label class="field">
<span class="field__label">Title override</span>
<input type="text" name="title" placeholder="leave blank to use theme name" />
</label>
<fieldset class="fieldset">
<legend>Directions</legend>
<div class="directions-grid">
<label class="check check--locked">
<input type="checkbox" checked disabled />
<span>→ Horizontal</span>
<span class="lock">REQ</span>
</label>
<label class="check check--locked">
<input type="checkbox" checked disabled />
<span>↓ Vertical</span>
<span class="lock">REQ</span>
</label>
<label class="check">
<input type="checkbox" name="diagonal" />
<span>↘ ↗ Diagonal</span>
</label>
<label class="check">
<input type="checkbox" name="reversed" />
<span>← ↑ ↖ ↙ Reversed</span>
</label>
</div>
<div class="fieldset__divider">
<label class="check">
<input type="checkbox" name="overlap" />
<span>Allow overlapping words</span>
</label>
</div>
</fieldset>
<div class="button-row">
<button class="btn" type="submit">✦ Generate</button>
<button class="btn btn--ghost" type="reset">Reset</button>
</div>
</div>
<aside class="preview-card" aria-label="Preview">
<div class="preview-card__head">
<span class="preview-card__title">Sample preview</span>
<span class="mono muted" id="preview-stats" style="font-size:11px;">12 × 12 · 10 words</span>
</div>
<div class="ws-grid" id="ws-grid" style="grid-template-columns: repeat(12, 1fr);"></div>
<div class="ws-words" id="ws-words"></div>
</aside>
</form>
{% endif %}
</section>
{% endblock %}
{% block scripts %}
<script>
(function () {
const themeSel = document.getElementById('theme');
const gridInput = document.getElementById('grid-size');
const countInput = document.getElementById('word-count-input');
const grid = document.getElementById('ws-grid');
const wordsEl = document.getElementById('ws-words');
const stats = document.getElementById('preview-stats');
if (!themeSel || !grid) return;
function selectedWords() {
const opt = themeSel.options[themeSel.selectedIndex];
return (opt.dataset.words || '').split(',').filter(Boolean);
}
function renderPreview() {
const N = Math.max(5, Math.min(25, parseInt(gridInput.value || '12', 10) || 12));
const want = Math.max(1, parseInt(countInput.value || '10', 10) || 10);
grid.style.gridTemplateColumns = `repeat(${N}, 1fr)`;
const words = selectedWords();
const grids = words
.map(w => w.replace(/^(mr|mrs|ms|miss|dr|sir|dame|lord|lady|master|captain|capt|cpt|professor|prof|saint|st)\.?\s+/gi, '')
.replace(/[^a-z0-9]/gi, '').toUpperCase())
.filter(g => g.length > 0);
const pool = (grids.join('') || 'WORDSMITH').replace(/[^A-Z]/g, '') || 'WORDSMITH';
let html = '';
for (let i = 0; i < N * N; i++) {
html += `<span>${pool[(i * 7 + 3) % pool.length]}</span>`;
}
grid.innerHTML = html;
const sample = grids.slice(0, Math.min(want, grids.length));
wordsEl.innerHTML = sample.map((g, i) =>
`<b>${g}</b>${i < sample.length - 1 ? '<span class="sep">·</span>' : ''}`
).join('');
stats.textContent = `${N} × ${N} · ${sample.length} word${sample.length === 1 ? '' : 's'}`;
}
themeSel.addEventListener('change', renderPreview);
gridInput.addEventListener('input', renderPreview);
countInput.addEventListener('input', renderPreview);
renderPreview();
})();
</script>
{% endblock %}