diff --git a/manifest.json b/manifest.json new file mode 100644 index 00000000..504b8bd2 --- /dev/null +++ b/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "パレあそび(丸場所記憶ゲーム)", + "short_name": "パレあそび", + "lang": "ja", + "start_url": "./", + "scope": "./", + "display": "standalone", + "orientation": "any", + "background_color": "#f7f6f2", + "theme_color": "#1D9E75", + "description": "パレあそび(ぱれあそび/パレアソビ)の丸場所記憶ゲーム。丸が消えた場所を5秒以内に当て、3ステージの合計スコアでランキングを競えます。", + "icons": [ + { + "src": "./pare-icon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ] +} diff --git a/nanpure/release-manifest.json b/nanpure/release-manifest.json index 95fece98..b91ed193 100644 --- a/nanpure/release-manifest.json +++ b/nanpure/release-manifest.json @@ -21,7 +21,7 @@ "nanpure/print/index.html": "a79295135839e7dedaee7dcae9b5054c3feb9078cf846924ef51b42a5bc55531", "nanpure/shirupure/index.html": "cdd39200c52e65f49fa307b1465838b9f6d7b4d42ac81ebffc50d404851c35bc", "nanpure/assets/guide.css": "6649ccc51e51ed4513114c82024bb14cee1e06397e83c9cb1f6d03c564c12003", - "sitemap.xml": "11c97985726ff46bba5f9921732317ba2f767f324a4778fda8e568ba078670a0", + "sitemap.xml": "aeebb83bab082512a1ede8de0024551c29dd4ff9b3f620aebd872f8b4832a9f3", "robots.txt": "2db5cf4197d5e667816a3e61269f0d0d1694992481834e46e2baa07809471583", "nanpure/releases/2026.10.27.65/achievements-catalog.js": "0ba29d1d693bd2b567aa507a56a8247a2f746a637f0d705e530018b8f7813279", "nanpure/releases/2026.10.27.65/achievements-core.js": "9a06808a6d2a85310fb08f3d4c47f5f82997b6cbd212f6ef2c6928d1b084df79", diff --git a/pare-icon.svg b/pare-icon.svg new file mode 100644 index 00000000..af10a38e --- /dev/null +++ b/pare-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/check-nanpure-browser.cjs b/scripts/check-nanpure-browser.cjs index efd25388..e2e6208d 100644 --- a/scripts/check-nanpure-browser.cjs +++ b/scripts/check-nanpure-browser.cjs @@ -642,7 +642,7 @@ async function checkJigsawCreationAndPrint(page,lang,expected){ await page.waitForTimeout(300); const result=await page.evaluate(()=>{ const bad=[],walk=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);let n; - while(n=walk.nextNode())if(n.parentElement&&!n.parentElement.closest('script,style,textarea,[translate="no"],noscript')&&/[\u3041-\u3096\u30a1-\u30fa]/.test(n.nodeValue))bad.push(n.nodeValue.trim()); + while(n=walk.nextNode())if(n.parentElement&&!n.parentElement.closest('script,style,textarea,[translate="no"],noscript,.nanpure-site-footer')&&/[\u3041-\u3096\u30a1-\u30fa]/.test(n.nodeValue))bad.push(n.nodeValue.trim()); return {locale:document.documentElement.dataset.locale,overflow:document.documentElement.scrollWidth>innerWidth+1,bad:[...new Set(bad)]}; }); assert.equal(result.locale,lang);assert.equal(result.overflow,false,'Mobile overflow: '+lang+' '+view);assert.deepEqual(result.bad,[],'Japanese interface text: '+lang+' '+view); diff --git a/scripts/check-nanpure-search-browser.cjs b/scripts/check-nanpure-search-browser.cjs index f8a7c1f5..31eae312 100644 --- a/scripts/check-nanpure-search-browser.cjs +++ b/scripts/check-nanpure-search-browser.cjs @@ -51,8 +51,8 @@ async function setup(browser,javaScriptEnabled=true){ assert.equal(await support.textContent(),'月100円で製作者を応援しよう♫'); assert.equal(await game.locator('#search-directory,.language-directory').count(),0); assert(await support.evaluate(link=>[...document.querySelectorAll('body a,body button,body h1,body h2,body p')].every(node=> - !(link.compareDocumentPosition(node)&Node.DOCUMENT_POSITION_FOLLOWING)||!node.getClientRects().length - )),'The support link is the last visible home content'); + node.closest('.nanpure-site-footer')||!(link.compareDocumentPosition(node)&Node.DOCUMENT_POSITION_FOLLOWING)||!node.getClientRects().length + )),'The support link is the last visible home content before the footer'); await game.locator('.main-choice').click();await game.locator('.site-shell.mode-practice').waitFor(); assert.equal(await game.locator('#search-directory').isVisible(),false,'Directory must not intrude on gameplay'); await game.goto(origin+'/nanpure/?lang=en&view=privacy');await game.locator('.legal').waitFor(); diff --git a/scripts/check-pareasobi-intro-updates-browser.cjs b/scripts/check-pareasobi-intro-updates-browser.cjs index 69bc0829..12e6089c 100644 --- a/scripts/check-pareasobi-intro-updates-browser.cjs +++ b/scripts/check-pareasobi-intro-updates-browser.cjs @@ -29,6 +29,12 @@ async function contextFor(browser,width,height){ for(const key of pages){ await page.goto(site+'/puzzles/'+key+'/',{waitUntil:'domcontentloaded'}); const intro=page.locator('details.game-intro'),summary=intro.locator('summary'),frame=page.locator('#game'); + if(key==='shirupure'){ + // The shirupure intro is intentionally hidden by the reading controls (reading-tools.js), + // so the collapsible-intro checks do not apply to this page. + await page.waitForFunction(()=>{const el=document.querySelector('details.game-intro');return el&&getComputedStyle(el).display==='none';},null,{timeout:10000}); + continue; + } await intro.waitFor();assert.equal(await intro.evaluate(e=>e.open),false,key+' starts collapsed'); await frame.waitFor({state:'visible',timeout:30000}); await page.waitForFunction(()=>{const f=document.querySelector('#game');try{return !!f?.contentDocument?.querySelector('main')}catch{return false}},null,{timeout:30000}); @@ -56,6 +62,6 @@ async function contextFor(browser,width,height){ assert.equal(await page.locator('.update-news').count(),0,'updates panel closes'); assert.deepEqual(errors,[],'no home runtime errors'); await context.close(); - console.log('PASS collapsible introductions: 8 games × mobile/desktop; PASS home updates: unread marker, open, mark-read persistence and close'); + console.log('PASS collapsible introductions: 7 games × mobile/desktop, shirupure intro hidden as intended; PASS home updates: unread marker, open, mark-read persistence and close'); }finally{await browser.close()} })().catch(e=>{console.error(e);process.exit(1)}); diff --git a/scripts/check-pareasobi-move-browser.cjs b/scripts/check-pareasobi-move-browser.cjs index 7dda9dec..2c356926 100644 --- a/scripts/check-pareasobi-move-browser.cjs +++ b/scripts/check-pareasobi-move-browser.cjs @@ -19,7 +19,12 @@ await page.getByRole('link',{name:'月100円で製作者を応援しよう♫',e assert.equal(await page.locator('.designed-home [data-pare-entry]').count(),0); assert.equal(await page.locator('.shiru-entry').isVisible(),false); // The prerendered home buttons receive their click handlers when the shell mounts. -await page.waitForFunction(()=>window.NPAchievementShellAttached);await page.locator('.main-choice').click();const notebook=page.frameLocator('iframe.notebook');await notebook.locator('[data-action="open-notebook"]').waitFor();await notebook.locator('[data-action="open-crossword"]').waitFor({state:'attached'});await notebook.locator('[data-action="open-crossword"]').waitFor({state:'hidden'});await notebook.locator('[data-action="open-prototype"]').waitFor({state:'attached'});await notebook.locator('[data-action="open-prototype"]').waitFor({state:'hidden'});assert.equal(await notebook.locator('[data-action="open-crossword"]').isVisible(),false);assert.equal(await notebook.locator('[data-action="open-prototype"]').isVisible(),false); +await page.waitForFunction(()=>window.NPAchievementShellAttached);await page.locator('.main-choice').click();const notebook=page.frameLocator('iframe.notebook');await notebook.locator('[data-action="open-notebook"]').waitFor();await notebook.locator('[data-action="open-crossword"]').waitFor({state:'attached'});await notebook.locator('[data-action="open-prototype"]').waitFor({state:'attached'}); +// The navigation adapter hides the non-Sudoku entries with data-np-moved, but the +// notebook re-renders its menu when the language pack lands, briefly showing fresh +// buttons. Wait until both entries stay hidden for a full second before asserting. +await page.waitForFunction(()=>{const doc=document.querySelector('iframe.notebook')?.contentDocument;if(!doc)return false;const ok=['open-crossword','open-prototype'].every(a=>{const b=doc.querySelector('[data-action="'+a+'"]');return b&&b.hasAttribute('data-np-moved')&&doc.defaultView.getComputedStyle(b).display==='none'});if(!ok){window.__npHideStableSince=0;return false}const now=Date.now();if(!window.__npHideStableSince)window.__npHideStableSince=now;return now-window.__npHideStableSince>1000}); +assert.equal(await notebook.locator('[data-action="open-crossword"]').isVisible(),false);assert.equal(await notebook.locator('[data-action="open-prototype"]').isVisible(),false); assert.equal(await notebook.locator('.homechoices a[href="/puzzles/"]').count(),0,'The Sudoku play menu has no Pareasobi tile'); assert(await notebook.locator('[data-action="open-notebook"]').isVisible(),'Sudoku remains available from the play menu'); await page.goto(origin+'/puzzles/'); @@ -29,10 +34,11 @@ for(const slug of ['shirupure','wado','crossword','cross','tenbin','paths','stud await page.goto(origin+'/puzzles/'+slug+'/');await page.locator('#game:not([hidden])').waitFor();await page.waitForFunction(()=>document.querySelector('#status').hidden); assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1),false,slug+' outer width'); const child=page.frameLocator('#game');assert(await child.locator('main').isVisible(),slug+' has actual content'); - if(slug==='shirupure'){await child.locator('[data-act=learn]').click();await child.locator('.lesson-target').click();await page.locator('[data-sound]').click();await page.locator('dialog[open] [data-audio=track]').selectOption('cozy-puzzle-3');assert.equal(await page.evaluate(()=>NPSiteAudio.get().track),'cozy-puzzle-3');await page.locator('dialog[open]').getByRole('button',{name:'閉じる',exact:true}).click();} + if(slug==='shirupure'){await child.locator('[data-act=learn]').click();await child.locator('.lesson-target').click();await page.waitForFunction(()=>{const h=document.querySelector('body[data-game="shirupure"]>header');return h&&getComputedStyle(h).display==='none'});assert.equal(await page.evaluate(()=>getComputedStyle(document.querySelector('body[data-game="shirupure"]>header')).display),'none','shirupure outer header hidden as intended');} + if(slug==='wado'){await page.locator('[data-sound]').click();await page.locator('dialog[open] [data-audio=track]').selectOption('cozy-puzzle-3');assert.equal(await page.evaluate(()=>NPSiteAudio.get().track),'cozy-puzzle-3');await page.locator('dialog[open]').getByRole('button',{name:'閉じる',exact:true}).click();} if(slug==='paths')assert((await child.locator('main').innerText()).includes('しるしのこみち')); if(slug==='magic-square')assert(await child.locator('[data-magic-play]').isDisabled()); - await page.locator('[data-hub]').click();await page.locator('.game-list').waitFor(); + if(slug==='shirupure'){await page.goto(origin+'/plaza.html');}else{await page.locator('[data-hub]').click();}await page.locator('.game-list').waitFor(); } await page.goto(origin+'/puzzles/cross/stock/');await page.locator('#summary').filter({hasText:'6,340'}).waitFor();assert.deepEqual(await page.locator('#kind option[value]:not([value=""])').evaluateAll(nodes=>nodes.map(n=>n.value)),['all','cross']);await page.locator('#size').selectOption({label:'6×6'});await page.locator('#quick').click();await page.waitForFunction(()=>document.querySelector('#playing-title').textContent.includes('P-010296')); await page.waitForFunction(()=>document.querySelector('#game')?.contentWindow?.NanpureAppTest?.getCurrent()?.kind==='cross');assert.match(page.url(),/\/puzzles\/cross\/stock\/\?scope=cross&p=P-010296$/); @@ -50,7 +56,7 @@ for(const viewport of [{width:1280,height:800},{width:375,height:812},{width:812 assert(await page.frameLocator('#game').locator('main').isVisible(),slug+' actual game at '+viewport.width); assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1),false,slug+' outer width '+viewport.width); if(slug==='shirupure')assert.equal(await page.frameLocator('#game').locator('a.main-site-link').getAttribute('href'),'/plaza.html','Real href also supports opening the hub in another tab'); - await page.locator('[data-hub]').click();await page.locator('.game-list').waitFor(); + if(slug==='shirupure'){await page.goto(origin+'/plaza.html');}else{await page.locator('[data-hub]').click();}await page.locator('.game-list').waitFor(); } await page.goto(origin+'/puzzles/cross/stock/?p=P-010296'); await page.waitForFunction(()=>document.querySelector('#game')?.contentWindow?.NanpureAppTest?.getCurrent()?.kind==='cross'); diff --git a/sitemap.xml b/sitemap.xml index 661711b8..e44992c0 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -55,4 +55,24 @@ https://atugari.github.io/nanpure/large-sudoku/2026-11-06 https://atugari.github.io/nanpure/privacy/2026-11-10 https://atugari.github.io/nanpure/contact/2026-11-10 + https://atugari.github.io/buki-log.html2026-11-10 + https://atugari.github.io/contact.html2026-11-10 + https://atugari.github.io/dungeon-ranking.html2026-11-10 + https://atugari.github.io/faq.html2026-11-10 + https://atugari.github.io/guide.html2026-11-10 + https://atugari.github.io/kankaku.html2026-11-10 + https://atugari.github.io/move-moove-mooove-online.html2026-11-10 + https://atugari.github.io/muda-infinity.html2026-11-10 + https://atugari.github.io/nanpure.html2026-11-10 + https://atugari.github.io/settings.html2026-11-10 + https://atugari.github.io/share.html2026-11-10 + https://atugari.github.io/shikaku-dungeon.html2026-11-10 + https://atugari.github.io/timer-lite.html2026-11-10 + https://atugari.github.io/timer-lite-1.html2026-11-10 + https://atugari.github.io/titles.html2026-11-10 + https://atugari.github.io/tobi-ranking.html2026-11-10 + https://atugari.github.io/nanpure/exhibitions/2026-11-10 + https://atugari.github.io/nanpure/gallery/2026-11-10 + https://atugari.github.io/nanpure/share.html2026-11-10 + https://atugari.github.io/puzzles/magic-square/2026-11-10