From eeb81d4fd589bc69275faf2f3de8881e49c7bbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Tue, 12 May 2026 15:08:26 +0200 Subject: [PATCH 1/2] chore: ignore .venv/ and .linkedin/ Both are local-only directories (Python virtualenv and LinkedIn skill cache) that shouldn't be tracked. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 571c06f..ad08752 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ node_modules/ !website/public/hmze-logo.png !docs/workflow-diagram.svg .playwright-mcp/ +.venv/ +.linkedin/ *:Zone.Identifier website/public/CONTRIBUTING.html website/public/CONTRIBUTING.de.html From e8f7d67360e7f6b6b3677085249bf3c3a6e78827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Wed, 13 May 2026 21:03:41 +0200 Subject: [PATCH 2/2] fix: ensure modal exists before opening on share-link first visit (#470) When a user opens a share-link like /#/anchor/ in a fresh tab, the router's handleRoute() called showAnchorDetails() before initApp's createModal() microtask had a chance to run. loadAnchorContent then crashed with "Cannot read properties of null (reading 'querySelector')", leaving the modal element missing and the homepage visible underneath. On a second visit the modal already existed in the DOM, so the bug appeared as "works on second click." Guard showAnchorDetails by calling the idempotent createModal() when the element is missing. Add a regression test that removes the modal element before calling showAnchorDetails and asserts the modal is created and opened. Co-Authored-By: Claude Opus 4.7 (1M context) --- website/src/components/anchor-modal.js | 8 ++++---- website/src/components/anchor-modal.test.js | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/website/src/components/anchor-modal.js b/website/src/components/anchor-modal.js index f5b3a95..15f58d4 100644 --- a/website/src/components/anchor-modal.js +++ b/website/src/components/anchor-modal.js @@ -356,10 +356,10 @@ export async function loadAnchorContent(anchorId) { } export function showAnchorDetails(anchorId) { - const modal = document.getElementById('anchor-modal') - if (modal) { - modal.dataset.currentAnchor = anchorId - } + // Share-link race: handleRoute may invoke us before initApp's createModal() + // microtask runs. createModal is idempotent. + const modal = document.getElementById('anchor-modal') || createModal() + modal.dataset.currentAnchor = anchorId openModal() return loadAnchorContent(anchorId) } diff --git a/website/src/components/anchor-modal.test.js b/website/src/components/anchor-modal.test.js index 5fe5c47..165fc28 100644 --- a/website/src/components/anchor-modal.test.js +++ b/website/src/components/anchor-modal.test.js @@ -155,6 +155,22 @@ describe('anchor-modal', () => { const content = document.getElementById('modal-content') expect(content.innerHTML).toContain('Failed to load') }) + + it('should not crash when modal element is missing (share-link race, #470)', async () => { + const existing = document.getElementById('anchor-modal') + if (existing) existing.remove() + + global.fetch.mockResolvedValue({ + ok: true, + text: async () => '= Test Anchor\n\nTest content', + }) + + await expect(showAnchorDetails('test-anchor')).resolves.not.toThrow() + + const modal = document.getElementById('anchor-modal') + expect(modal).not.toBeNull() + expect(modal.classList.contains('hidden')).toBe(false) + }) }) describe('umbrella anchors', () => {