- commit
- 4d0a61d16442bb7e62631b768117db274aa59a07
- parent
- 13a332ff9c3ef12ed7dd9e6d351cb7da7be5f11a
- author
- Dami <dami.etoile@gmail.com>
| M | README.md | 2 | +- |
| A | lib/ | 13 | ++++++ |
| M | routes/ | 3 | +- |
| A | routes/ | 70 | ++++++++++++++++++++++++++++++ |
| A | routes/ | 14 | ++++++ |
| M | routes/ | 67 | +---------------------------- |
| M | routes/ | 11 | +---- |
| M | routes/ | 3 | +- |
| A | tests/ | 25 | +++++++++++ |
@@ -0,0 +1,13 @@
1+export function buildDailyPath(date?: string): string {2+ return date ? `/d/${date}` : "/d";3+}4+5+export function buildLegacyDailyPath(date?: string): string {6+ return date ? `/daily/${date}` : "/daily";7+}8+9+export function isDailyPathname(pathname: string): boolean {10+ return pathname === "/d" || pathname.startsWith("/d/") ||11+ pathname === "/daily" ||12+ pathname.startsWith("/daily/");13+}@@ -1,3 +1,4 @@
1+import { isDailyPathname } from "@/lib/site/paths.ts";12 import { define } from "../utils.ts";23 import {34 buildPageTitle,@@ -7,7 +8,7 @@ import {
78 } from "@/lib/site/brand.ts";89 910 export default define.page(function App({ Component, url }) {10- const isDailyPage = url.pathname.startsWith("/daily");11+ const isDailyPage = isDailyPathname(url.pathname);1112 const pageTitle = isDailyPage1213 ? buildPageTitle("Daily Puzzle")1314 : buildPageTitle();@@ -0,0 +1,70 @@
1+import { getPuzzleByDate } from "@/lib/shikaku/catalog.ts";2+import { buildDailyPath } from "@/lib/site/paths.ts";3+import { define } from "../../utils.ts";4+import ShikakuGame from "../../islands/ShikakuGame.tsx";5+6+export const handler = define.handlers((ctx) => {7+ const puzzle = getPuzzleByDate(ctx.params.date);8+9+ if (!puzzle) {10+ return new Response("Puzzle not found", { status: 404 });11+ }12+13+ const [year, month, day] = puzzle.date.split("-").map(Number);14+ const currentDate = new Date(Date.UTC(year, month - 1, day));15+ const previousDate = new Date(currentDate);16+ previousDate.setUTCDate(previousDate.getUTCDate() - 1);17+ const nextDate = new Date(currentDate);18+ nextDate.setUTCDate(nextDate.getUTCDate() + 1);19+20+ return {21+ data: {22+ puzzle,23+ previousDate: previousDate.toISOString().slice(0, 10),24+ nextDate: nextDate.toISOString().slice(0, 10),25+ },26+ };27+});28+29+export default define.page<typeof handler>(({ data }) => {30+ return (31+ <main class="shikaku-play-shell">32+ <div class="shikaku-play-frame">33+ <header class="shikaku-play-header">34+ <div class="shikaku-play-header__row">35+ <div class="shikaku-play-day-nav shikaku-play-day-nav--with-home">36+ <a37+ class="shikaku-play-nav shikaku-play-nav--icon"38+ href="/"39+ aria-label="Back home"40+ >41+ ⌂42+ </a>43+ <a44+ class="shikaku-play-nav shikaku-play-nav--icon"45+ href={buildDailyPath(data.previousDate)}46+ aria-label="Previous day"47+ >48+ ‹49+ </a>50+ <div>51+ <p class="shikaku-play-kicker">Daily puzzle</p>52+ <h1 class="shikaku-play-title">{data.puzzle.date}</h1>53+ </div>54+ <a55+ class="shikaku-play-nav shikaku-play-nav--icon"56+ href={buildDailyPath(data.nextDate)}57+ aria-label="Next day"58+ >59+ ›60+ </a>61+ </div>62+ </div>63+ <p class="shikaku-play-hint">Drag. One clue.</p>64+ </header>65+66+ <ShikakuGame puzzle={data.puzzle} />67+ </div>68+ </main>69+ );70+});@@ -0,0 +1,14 @@
1+import { listAvailablePuzzleDates } from "@/lib/shikaku/catalog.ts";2+import { buildDailyPath } from "@/lib/site/paths.ts";3+import { define } from "../../utils.ts";4+5+export const handler = define.handlers((ctx) => {6+ const dates = listAvailablePuzzleDates();7+ const latest = dates.at(-1);8+9+ if (!latest) {10+ return new Response("No puzzles available", { status: 404 });11+ }12+13+ return ctx.redirect(buildDailyPath(latest));14+});@@ -1,69 +1,6 @@
1-import { getPuzzleByDate } from "@/lib/shikaku/catalog.ts";1+import { buildDailyPath } from "@/lib/site/paths.ts";22 import { define } from "../../utils.ts";3-import ShikakuGame from "../../islands/ShikakuGame.tsx";43 54 export const handler = define.handlers((ctx) => {6- const puzzle = getPuzzleByDate(ctx.params.date);7-8- if (!puzzle) {9- return new Response("Puzzle not found", { status: 404 });10- }11-12- const [year, month, day] = puzzle.date.split("-").map(Number);13- const currentDate = new Date(Date.UTC(year, month - 1, day));14- const previousDate = new Date(currentDate);15- previousDate.setUTCDate(previousDate.getUTCDate() - 1);16- const nextDate = new Date(currentDate);17- nextDate.setUTCDate(nextDate.getUTCDate() + 1);18-19- return {20- data: {21- puzzle,22- previousDate: previousDate.toISOString().slice(0, 10),23- nextDate: nextDate.toISOString().slice(0, 10),24- },25- };26-});27-28-export default define.page<typeof handler>(({ data }) => {29- return (30- <main class="shikaku-play-shell">31- <div class="shikaku-play-frame">32- <header class="shikaku-play-header">33- <div class="shikaku-play-header__row">34- <div class="shikaku-play-day-nav shikaku-play-day-nav--with-home">35- <a36- class="shikaku-play-nav shikaku-play-nav--icon"37- href="/"38- aria-label="Back home"39- >40- ⌂41- </a>42- <a43- class="shikaku-play-nav shikaku-play-nav--icon"44- href={`/daily/${data.previousDate}`}45- aria-label="Previous day"46- >47- ‹48- </a>49- <div>50- <p class="shikaku-play-kicker">Daily puzzle</p>51- <h1 class="shikaku-play-title">{data.puzzle.date}</h1>52- </div>53- <a54- class="shikaku-play-nav shikaku-play-nav--icon"55- href={`/daily/${data.nextDate}`}56- aria-label="Next day"57- >58- ›59- </a>60- </div>61- </div>62- <p class="shikaku-play-hint">Drag. One clue.</p>63- </header>64-65- <ShikakuGame puzzle={data.puzzle} />66- </div>67- </main>68- );5+ return ctx.redirect(buildDailyPath(ctx.params.date));696 });@@ -1,13 +1,6 @@
1-import { listAvailablePuzzleDates } from "@/lib/shikaku/catalog.ts";1+import { buildDailyPath } from "@/lib/site/paths.ts";22 import { define } from "../../utils.ts";33 44 export const handler = define.handlers((ctx) => {5- const dates = listAvailablePuzzleDates();6- const latest = dates.at(-1);7-8- if (!latest) {9- return new Response("No puzzles available", { status: 404 });10- }11-12- return ctx.redirect(`/daily/${latest}`);5+ return ctx.redirect(buildDailyPath());136 });@@ -1,4 +1,5 @@
11 import { listAvailablePuzzleDates } from "@/lib/shikaku/catalog.ts";2+import { buildDailyPath } from "@/lib/site/paths.ts";23 import { define } from "../utils.ts";34 45 export default define.page(function Home() {@@ -25,7 +26,7 @@ export default define.page(function Home() {
2526 <div class="shikaku-home__actions">2627 <a2728 class="shikaku-home__action shikaku-home__action--gold"28- href={`/daily/${latest}`}29+ href={buildDailyPath(latest)}2930 >3031 <span class="shikaku-home__action-kicker">/ START</span>3132 <strong>Play</strong>@@ -0,0 +1,25 @@
1+import { assertEquals } from "@std/assert";2+3+import {4+ buildDailyPath,5+ buildLegacyDailyPath,6+ isDailyPathname,7+} from "@/lib/site/paths.ts";8+9+Deno.test("buildDailyPath uses the /d prefix", () => {10+ assertEquals(buildDailyPath(), "/d");11+ assertEquals(buildDailyPath("2026-04-17"), "/d/2026-04-17");12+});13+14+Deno.test("buildLegacyDailyPath keeps /daily redirects available", () => {15+ assertEquals(buildLegacyDailyPath(), "/daily");16+ assertEquals(buildLegacyDailyPath("2026-04-17"), "/daily/2026-04-17");17+});18+19+Deno.test("isDailyPathname recognizes both current and legacy daily routes", () => {20+ assertEquals(isDailyPathname("/d"), true);21+ assertEquals(isDailyPathname("/d/2026-04-17"), true);22+ assertEquals(isDailyPathname("/daily"), true);23+ assertEquals(isDailyPathname("/daily/2026-04-17"), true);24+ assertEquals(isDailyPathname("/"), false);25+});