From 431e36b4a105f19aea248c77b809ca43c10b0702 Mon Sep 17 00:00:00 2001 From: ken Date: Wed, 28 Oct 2026 23:26:04 +0900 Subject: [PATCH 1/4] Revert "growth(catalog): expose dual-store install links for four priority apps (#24)" This reverts commit 28379c4ac0181816b184ae5a509587815f7f171c. --- apps.html | 56 +++++-------------- apps_data.json | 14 ++--- scripts/build_catalog.py | 20 +------ tests/catalog-growth.test.mjs | 36 +----------- tests/catalog-positioning-regression.test.mjs | 27 +++------ 5 files changed, 33 insertions(+), 120 deletions(-) diff --git a/apps.html b/apps.html index 48c23ebf..d1e29bc8 100644 --- a/apps.html +++ b/apps.html @@ -25,11 +25,6 @@ .app-card h3 { font-size:1rem; margin-bottom:.15rem; } .app-card p { font-size:.82rem; color:var(--sub); display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } .store-badge { display:inline-block; margin-top:.4rem; font-size:.78rem; font-weight:600; color:var(--accent); } - .app-card-dual .app-card-body { min-width:0; } - .store-links { display:flex; flex-wrap:wrap; gap:.45rem; margin-top:.55rem; } - .store-link { color:var(--accent); text-decoration:underline; font-size:.78rem; font-weight:700; padding:.3rem .2rem; } - .store-link:focus-visible { outline:2px solid var(--accent); outline-offset:2px; } - footer { text-align:center; color:var(--sub); font-size:.85rem; padding:2rem; } footer a { color:var(--accent); } /* Browse purpose or search without hiding cards from non-JS crawlers. */ @@ -107,17 +102,14 @@

SheetDecode: Excel Formula Fix

Google Play → -
+ Debate Me - AI Debate Practice icon -
+ RoastMe - Community Roasts icon
@@ -134,17 +126,14 @@

Feels Like Weather & Comfort

Google Play →
-
+ Dotchi: This or That Polls icon -
+ FileForce - PDF & File Manager icon
@@ -217,17 +206,14 @@

Trend - Anime & Game Picker

Google Play →
-
+ Blur: Guess the Image icon -
+ BrainBurst - Daily Trivia Quiz icon
@@ -588,17 +574,14 @@

Travel Safety Map & Scam Alert

Google Play →
-
+ Instant Judge: Vote in 3s icon -
+ KeyClash: Typing Battle icon
@@ -895,18 +878,9 @@

Chaos - Stranger Missions

buttons.forEach(b => b.setAttribute('aria-pressed', String(b === button))); fire('catalog_filter_used', {goal, results: render()}); })); - cards.forEach(card => { - const links = card.querySelectorAll?.('a[data-store]') || []; - if (links.length) { - links.forEach(link => link.addEventListener('click', () => { - fire('catalog_store_click', {app: slug(card), goal, source: 'apps_catalog', store: link.dataset.store}); - })); - } else { - card.addEventListener('click', () => { - fire('catalog_store_click', {app: slug(card), goal, source: 'apps_catalog', store: 'google_play'}); - }); - } - }); + cards.forEach(card => card.addEventListener('click', () => { + fire('catalog_store_click', {app: slug(card), goal, source: 'apps_catalog'}); + })); render(); })(); diff --git a/apps_data.json b/apps_data.json index 1c5bec2d..2d92f41f 100644 --- a/apps_data.json +++ b/apps_data.json @@ -18,8 +18,7 @@ "pkg": "jp.appdevelopsk.debate_me", "title": "Debate Me - AI Debate Practice", "desc": "Practice debating with an AI opponent, get feedback and share your results", - "installs": 28, - "ios_app_id": 6760968208 + "installs": 28 }, { "app": "roast-me", @@ -40,8 +39,7 @@ "pkg": "com.dotchi.dotchi_app", "title": "Dotchi: This or That Polls", "desc": "Pick A or B on everyday questions. Vote, see results and compare opinions with friends.", - "installs": 22, - "ios_app_id": 6760481027 + "installs": 22 }, { "app": "fileforce", @@ -111,8 +109,7 @@ "pkg": "com.blurapp.blur_app", "title": "Blur: Guess the Image", "desc": "Guess the photo as it slowly unblurs — a fun image guessing brain game", - "installs": 15, - "ios_app_id": 6759920723 + "installs": 15 }, { "app": "brain-burst", @@ -474,8 +471,7 @@ "pkg": "com.instantjudge.instant_judge", "title": "Instant Judge: Vote in 3s", "desc": "Vote on community dilemmas, post your own and see results when revealed", - "installs": 13, - "ios_app_id": 6759920352 + "installs": 13 }, { "app": "key-clash", @@ -703,4 +699,4 @@ "desc": "Daily missions from strangers to break your routine and rediscover the world", "installs": null } -] +] \ No newline at end of file diff --git a/scripts/build_catalog.py b/scripts/build_catalog.py index 7830f903..9e34827e 100644 --- a/scripts/build_catalog.py +++ b/scripts/build_catalog.py @@ -51,21 +51,6 @@
""" -# Two independent links only for apps with a verified owner-maintained App Store ID. -# Do not put an anchor inside another anchor; keep crawlers and keyboard users safe. -CARD_DUAL = """
- {title} icon -
-

{title}

-

{desc}

- -
-
-""" - LANGS = ["en", "ja", "zh", "zh_TW", "ko", "es", "pt", "fr", "de", "it", "ru", "ar", "th", "id", "tr", "nl", "vi"] @@ -160,9 +145,8 @@ def load_counts(apps, refresh_total): def build_apps_html(t, apps, n): - cards = "".join((CARD_DUAL if a.get("ios_app_id") else CARD).format( - pkg=a["pkg"], app=a["app"], title=html.escape(a["title"]), - desc=html.escape(a["desc"]), ios_app_id=a.get("ios_app_id", "")) + cards = "".join(CARD.format(pkg=a["pkg"], app=a["app"], title=html.escape(a["title"]), + desc=html.escape(a["desc"])) for a in apps if not a.get("hidden")) t = sub1(r'(
\n).*?(
\n )', lambda m: m.group(1) + cards + m.group(2), t, "apps.html の grid", re.S) diff --git a/tests/catalog-growth.test.mjs b/tests/catalog-growth.test.mjs index 5422788d..32e5fa10 100644 --- a/tests/catalog-growth.test.mjs +++ b/tests/catalog-growth.test.mjs @@ -12,15 +12,9 @@ test('catalog search retains 92 real indexed store cards and passes install refe const visible=data.filter(x=>!x.hidden); const tags=[...catalog.matchAll(//g)]; - assert.equal(tags.length+dual.length,visible.length); + assert.equal(tags.length,visible.length); for (const entry of visible) { - if (entry.ios_app_id) { - assert.ok(catalog.includes('id='+entry.pkg),entry.app+' Play destination missing'); - assert.ok(catalog.includes('apps.apple.com/app/id'+entry.ios_app_id+'?ct=apps_catalog_'+entry.app),entry.app+' App Store destination missing'); - } else { - assert.ok(tags.some(t=>t[1].includes('id='+entry.pkg)),entry.app); - } + assert.ok(tags.some(t=>t[1].includes('id='+entry.pkg)),entry.app); } assert.ok(tags.every(t=>t[1].includes('referrer=utm_source%3Dappdevelopsk'))); assert.match(catalog,/id="find-app"/); @@ -57,28 +51,6 @@ test('filters work client-side without tracking raw search text',()=>{ assert.ok(events.some(e=>e[1]==='catalog_store_click'&&e[2].app==='blur')); }); -test('dual-store cards log distinct app-store and Play clicks, not a whole-card click',()=>{ - const source=[...catalog.matchAll(/]*>([\s\S]*?)<\/script>/g)].map(m=>m[1]).find(x=>x.includes('catalog_store_click')); - assert.ok(source); - function el(extra={}){return {events:{},hidden:false,...extra,addEventListener(name,fn){this.events[name]=fn;},setAttribute(key,val){this[key]=val;}}} - const apple=el({dataset:{store:'app_store'}}), play=el({dataset:{store:'google_play'}}); - const dual=el({ - textContent:'Debate Me - AI Debate Practice', - querySelector:()=>({getAttribute:()=> 'icons/apps/debate-me.png'}), - querySelectorAll:()=>[play,apple], - }); - const input=el({value:''}),count=el({textContent:''}),empty=el(); - const buttons=['all','quick-games','habits','decisions','ai'].map(goal=>el({dataset:{goal}})); - const events=[]; - runInNewContext(source,{document:{getElementById(id){return {'find-app':input,'finder-count':count,'finder-empty':empty}[id];},querySelectorAll(q){return q==='.grid .app-card'?[dual]:buttons;}},window:{gtag(...args){events.push(args);}}}); - assert.equal(dual.events.click,undefined,'card body must not masquerade as a store click'); - apple.events.click(); - play.events.click(); - const tracked=events.filter(e=>e[1]==='catalog_store_click'); - assert.deepEqual(tracked.map(e=>e[2].store),['app_store','google_play']); - assert.ok(tracked.every(e=>e[2].app==='debate-me'&&e[2].source==='apps_catalog')); -}); - test('guide landing pages link only to listed published applications and are discoverable',()=>{ for(const slug of guides){ const page=read('guides/'+slug+'/index.html'); @@ -129,9 +101,7 @@ test('public Dotchi catalog aligns with its real two-choice voting product', () assert.match(app.title, /Dotchi: This or That Polls/); assert.match(app.desc, /Pick A or B on everyday questions/); assert.doesNotMatch(app.title + app.desc, /anime|manga|Naruto|Goku/i); - const img = catalog.indexOf('src="icons/apps/twotaku.png"'); - const card = img >= 0 ? catalog.slice(catalog.lastIndexOf('
',img),img+800) : ''; - + const card = catalog.match(//)?.[0]; assert.ok(card, 'missing indexed Dotchi catalog card'); assert.match(card, /Dotchi: This or That Polls/); assert.match(card, /Vote, see results and compare opinions with friends/); diff --git a/tests/catalog-positioning-regression.test.mjs b/tests/catalog-positioning-regression.test.mjs index fe3a1fad..cd02b6fc 100644 --- a/tests/catalog-positioning-regression.test.mjs +++ b/tests/catalog-positioning-regression.test.mjs @@ -13,27 +13,16 @@ const highPriority = { }; const encode = value => value.replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,'''); -test('catalog cards reproduce canonical titles with one or two valid store destinations', () => { +test('catalog cards reproduce the canonical visible app titles and descriptions', () => { const visible = apps.filter(a=>!a.hidden); - assert.equal((page.match(/class="app-card"/g)||[]).length+ - (page.match(/class="app-card app-card-dual"/g)||[]).length,visible.length); + const cards = [...page.matchAll(/([^<]+)<\/h3>\s*

([^<]*)<\/p>[\s\S]*?<\/a>/g)]; + assert.equal(cards.length,visible.length); for (const a of visible) { - const image='src="icons/apps/'+a.app+'.png"'; - const idx=page.indexOf(image); - assert.ok(idx>=0,a.app+' missing catalog icon/card'); - assert.equal(page.split(image).length-1,1,a.app+' duplicate catalog card'); - const start=page.lastIndexOf(a.ios_app_id ? '

' : '' : '\n ',idx); - assert.ok(start>=0&&end>idx,a.app+' card boundary missing'); - const card=page.slice(start,end); - assert.ok(card.includes('

'+encode(a.title)+'

'),a.app+' title drift'); - assert.ok(card.includes('

'+encode(a.desc)+'

'),a.app+' description drift'); - assert.ok(card.includes('id='+a.pkg),a.app+' Play package drift'); - assert.match(card,/referrer=utm_source%3Dappdevelopsk/); - if (a.ios_app_id){ - assert.ok(card.includes('apps.apple.com/app/id'+a.ios_app_id+'?ct=apps_catalog_'+a.app),a.app+' iOS CTA missing'); - assert.ok(card.includes('data-store="app_store"'),a.app+' iOS store label missing'); - } + const match=cards.filter(m=>m[1].includes('id='+a.pkg)); + assert.equal(match.length,1,a.app+' missing or duplicate'); + assert.equal(match[0][2],encode(a.title),a.app+' title drift'); + assert.equal(match[0][3],encode(a.desc),a.app+' description drift'); + assert.match(match[0][1],/referrer=utm_source%3Dappdevelopsk/); } }); test('five high-traffic-intent entries describe genuinely implemented core value',()=>{ From 49d922eccf9094c5fccf4579ac2e05859491ce33 Mon Sep 17 00:00:00 2001 From: ken Date: Wed, 28 Oct 2026 23:26:15 +0900 Subject: [PATCH 2/4] Revert "growth(web): add App Store discovery links for Instant Judge (#22)" This reverts commit 4d0be126f788a77f8861c1d69b9aa10d97271754. --- scripts/check_app_discovery_live.sh | 2 - support/instant-judge/index.html | 46 ++++++++++----------- tests/instant-judge-support-growth.test.mjs | 17 -------- 3 files changed, 22 insertions(+), 43 deletions(-) diff --git a/scripts/check_app_discovery_live.sh b/scripts/check_app_discovery_live.sh index cfaea74e..d2c59154 100644 --- a/scripts/check_app_discovery_live.sh +++ b/scripts/check_app_discovery_live.sh @@ -86,8 +86,6 @@ fetch '/support/instant-judge/' 'instant-judge.html' grep -Fq 'Vote on everyday dilemmas, create your own post' "$tmp/instant-judge.html" || { echo 'FAIL Instant Judge hero is stale' >&2; exit 1; } grep -Fq 'data-position="hero"' "$tmp/instant-judge.html" || { echo 'FAIL Instant Judge first-screen Play CTA missing' >&2; exit 1; } grep -Fq 'support_instant_judge_hero' "$tmp/instant-judge.html" || { echo 'FAIL Instant Judge campaign tagging missing' >&2; exit 1; } -grep -Fq 'apps.apple.com/app/id6759920352?ct=support_instant_judge_hero' "$tmp/instant-judge.html" || { echo 'FAIL Instant Judge hero App Store CTA missing' >&2; exit 1; } -grep -Fq 'apps.apple.com/app/id6759920352?ct=support_instant_judge_details' "$tmp/instant-judge.html" || { echo 'FAIL Instant Judge details App Store CTA missing' >&2; exit 1; } grep -Fq 'Results appear at their reveal time' "$tmp/instant-judge.html" || { echo 'FAIL Instant Judge reveal copy missing' >&2; exit 1; } if grep -Eiq 'Thousands of scenarios|Real-time results|Many features work offline' "$tmp/instant-judge.html"; then echo 'FAIL Instant Judge advertises unsupported immediate results or scale' >&2; exit 1 diff --git a/support/instant-judge/index.html b/support/instant-judge/index.html index bdd89927..6bf8f424 100644 --- a/support/instant-judge/index.html +++ b/support/instant-judge/index.html @@ -5,7 +5,7 @@ Instant Judge – Vote on Dilemmas & Community Results | SK APPS - +