dm / commit
Deploy to apps.fuzzyworld.net with fuzzyworld-tools
README.md | 8 ++++---- package.json | 10 +++++++++- public/index.html | 24 +++++++----------------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index fa03744..e489b10 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An AI-powered solo tabletop RPG app that runs entirely in your browser, using the **Cypher System** rules. Your API key never leaves your device — all requests go directly from your browser to the Anthropic API. -**Live:** [pavelsafronov.gitlab.io/dm/](https://pavelsafronov.gitlab.io/dm/) +**Live:** [apps.fuzzyworld.net/dm/](https://apps.fuzzyworld.net/dm/) --- @@ -26,7 +26,7 @@ The game is powered by `claude-haiku-4-5-20251001` and uses Claude's tool-use AP - **Dice roller** — d4 through d100, configurable count - **World Notes** — orange-bordered GM meta-context panel for providing background lore, world physics, and house rules that Claude treats as absolute truth - **Share links** — generate an encrypted `#k=…` URL so someone else can use the app with your API key; they enter a passphrase you share separately; the key is never in the URL -- **Report Issue** — in-game button that collects debug info (character state, recent errors, session cost) and opens a pre-filled GitLab issue or Service Desk email +- **Report Issue** — in-game button that collects debug info (character state, recent errors, session cost) and opens a pre-filled bug-report email ## Running locally @@ -45,7 +45,7 @@ Requires Node.js. No build step — the entire app is a single `public/index.htm | Share links | AES-GCM encrypted blob in the URL hash (`#k=`); hash is never sent to the server | | Session state | `localStorage` — character sheet, conversation history, rendered HTML | | Analytics | `metadata.user_id` in each API request carries `p_<playerId>:g_<gameId>` for Anthropic Console | -| Deployment | GitLab Pages — push to `main`, CI deploys `public/` automatically | +| Deployment | apps.fuzzyworld.net — `npm run deploy` publishes `public/` | ### Why a single file? @@ -55,7 +55,7 @@ The tradeoffs are real — no type safety, no module splitting, global state eve ## Reporting issues -Use the **🐛 Report** button in the top bar — it collects debug info automatically. Or open an issue directly at [gitlab.com/PavelSafronov/dm/-/issues](https://gitlab.com/PavelSafronov/dm/-/issues). +Use the **🐛 Report** button in the top bar — it collects debug info automatically. ## License diff --git a/package.json b/package.json index 5999907..08d628c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,14 @@ "version": "1.0.0", "description": "AI-powered solo D&D app powered by Claude", "scripts": { - "dev": "npx serve public" + "dev": "npx serve public", + "deploy": "npx --yes -p git+https://code.fuzzyworld.net/fuzzyworld-tools.git#semver:^1.0.0 fw deploy" + }, + "fuzzyworld": { + "app": "dm", + "title": "Solo Dungeon Master", + "checks": [], + "build": "true", + "out": "public" } } diff --git a/public/index.html b/public/index.html index 8508ccf..5865efc 100644 --- a/public/index.html +++ b/public/index.html @@ -1270,7 +1270,7 @@ body { <div class="creation-title"><img src="dice.png" alt="" style="height:0.85em;vertical-align:middle;margin-right:0.2em;filter:invert(1) sepia(1) saturate(2) hue-rotate(5deg) brightness(0.9)"> Solo Dungeon Master</div> <div class="creation-sub">Powered by Claude — your story begins here</div> <p style="font-size:0.85rem;color:var(--text-dim);max-width:560px;margin:10px auto 0;line-height:1.6;text-align:center"> - An AI-powered solo tabletop RPG running entirely in your browser. Build your character below, then Claude becomes your Game Master — narration, rules, and consequences — for a full <a href="https://cyphersrd.quest" target="_blank" rel="noopener" style="color:var(--gold-dim)">Cypher System</a> adventure. Your API key never leaves your device. <a href="https://gitlab.com/PavelSafronov/dm/-/blob/main/README.md" target="_blank" rel="noopener" style="color:var(--gold-dim)">README →</a> + An AI-powered solo tabletop RPG running entirely in your browser. Build your character below, then Claude becomes your Game Master — narration, rules, and consequences — for a full <a href="https://cyphersrd.quest" target="_blank" rel="noopener" style="color:var(--gold-dim)">Cypher System</a> adventure. Your API key never leaves your device. <a href="https://code.fuzzyworld.net/dm/-/blob/main/README.md" target="_blank" rel="noopener" style="color:var(--gold-dim)">README →</a> </p> <p class="char-sentence" id="char-sentence">I am a <em>___</em> <em>___</em> who <em>___</em></p> @@ -1327,7 +1327,7 @@ body { </p> <p class="attribution"> - <a href="https://gitlab.com/PavelSafronov/dm" target="_blank" rel="noopener">Source & README</a> · + <a href="https://code.fuzzyworld.net/dm/" target="_blank" rel="noopener">Source & README</a> · <a href="https://www.flaticon.com/authors/icongeek26" target="_blank" rel="noopener">Dice icon by Icongeek26</a> from <a href="https://www.flaticon.com/" target="_blank" rel="noopener">Flaticon</a> </p> @@ -1474,7 +1474,7 @@ body { <div id="dice-result"></div> <div id="dice-result-detail"></div> <p class="attribution" style="margin-top:10px"> - <a href="https://gitlab.com/PavelSafronov/dm" target="_blank" rel="noopener">Source & README</a> · + <a href="https://code.fuzzyworld.net/dm/" target="_blank" rel="noopener">Source & README</a> · <a href="https://www.flaticon.com/authors/icongeek26" target="_blank" rel="noopener">Dice icon by Icongeek26</a> / <a href="https://www.flaticon.com/" target="_blank" rel="noopener">Flaticon</a> </p> @@ -1539,12 +1539,11 @@ body { <div class="report-actions"> <button class="share-btn share-btn-primary" id="report-email-btn">📧 Email — no account needed</button> - <button class="share-btn share-btn-secondary" id="report-gitlab-btn">🔗 GitLab Issue</button> <button class="share-btn share-btn-secondary" id="report-copy-btn">📋 Copy info</button> <button class="share-btn share-btn-secondary" id="report-close-btn">Cancel</button> </div> <p style="font-size:0.72rem;color:var(--text-dim);margin-top:10px;font-style:italic"> - Email creates a support ticket with no account required. GitLab Issue requires a free GitLab account. + Email needs no account. Copy info if you would rather send it another way. </p> </div> </div> @@ -1558,7 +1557,7 @@ body { <div class="setup-logo"><img src="dice.png" alt="" style="height:1em;vertical-align:middle;margin-right:0.3em;filter:invert(1) sepia(1) saturate(2) hue-rotate(5deg) brightness(0.9)"> Solo Dungeon Master</div> <p class="setup-sub">Powered by Claude — your story begins here</p> <p style="font-size:0.8rem;color:var(--text-dim);margin:0 0 18px;line-height:1.6"> - An AI-powered solo RPG in your browser. Claude acts as your Game Master for a full <a href="https://cyphersrd.quest" target="_blank" rel="noopener" style="color:var(--gold-dim)">Cypher System</a> adventure — narration, rules, and consequences all generated live. <a href="https://gitlab.com/PavelSafronov/dm/-/blob/main/README.md" target="_blank" rel="noopener" style="color:var(--gold-dim)">README →</a> + An AI-powered solo RPG in your browser. Claude acts as your Game Master for a full <a href="https://cyphersrd.quest" target="_blank" rel="noopener" style="color:var(--gold-dim)">Cypher System</a> adventure — narration, rules, and consequences all generated live. <a href="https://code.fuzzyworld.net/dm/-/blob/main/README.md" target="_blank" rel="noopener" style="color:var(--gold-dim)">README →</a> </p> <div style="font-family:'Cinzel',serif;font-size:0.75rem;letter-spacing:0.12em;color:var(--gold);text-transform:uppercase;margin-bottom:8px">Anthropic API Key</div> @@ -1779,7 +1778,7 @@ async function decryptApiKey(blob, passphrase) { // 'A:' prefix or legacy (no prefix) — both use AES-GCM const b = blob.startsWith('A:') ? blob : 'A:' + blob; if (!window.isSecureContext) - throw new Error('This link uses strong encryption and requires HTTPS to open. Use your GitLab Pages URL.'); + throw new Error('This link uses strong encryption and requires HTTPS to open. Open it from https://apps.fuzzyworld.net/dm/.'); return aesDecrypt(b, passphrase); } @@ -3511,8 +3510,7 @@ document.getElementById('setup-unlock-btn').addEventListener('click', async () = // ── REPORT MODAL HANDLERS ───────────────────────────── -const SERVICE_DESK_EMAIL = 'contact-project+pavelsafronov-dm-82624720-issue-@incoming.gitlab.com'; -const GITLAB_ISSUES_URL = 'https://gitlab.com/PavelSafronov/dm/-/issues/new'; +const SERVICE_DESK_EMAIL = 'bugs-k7q4+dm@fuzzyworld.net'; function openReportModal() { document.getElementById('report-desc').value = ''; @@ -3542,14 +3540,6 @@ document.getElementById('report-email-btn').addEventListener('click', () => { window.open(`mailto:${SERVICE_DESK_EMAIL}?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`); }); -document.getElementById('report-gitlab-btn').addEventListener('click', () => { - const desc = document.getElementById('report-desc').value.trim(); - const body = buildDebugInfo(desc); - const title = desc ? desc.split('\n')[0].slice(0, 80) : 'Bug report'; - const params = new URLSearchParams({ 'issue[title]': title, 'issue[description]': body }); - window.open(`${GITLAB_ISSUES_URL}?${params}`); -}); - document.getElementById('report-copy-btn').addEventListener('click', () => { const desc = document.getElementById('report-desc').value.trim(); navigator.clipboard.writeText(buildDebugInfo(desc)).then(() => {