diff --git a/src/features/flight-starfield/model/starfieldModel.ts b/src/features/flight-starfield/model/starfieldModel.ts index 5a33207..90bed0e 100644 --- a/src/features/flight-starfield/model/starfieldModel.ts +++ b/src/features/flight-starfield/model/starfieldModel.ts @@ -1,7 +1,7 @@ -import { FLIGHT_STARFIELD_TUNING } from '@/shared/config/starfield'; -import { clamp, randomInRange } from '@/shared/lib/math/number'; +import { FLIGHT_STARFIELD_TUNING } from "@/shared/config/starfield"; +import { clamp, randomInRange } from "@/shared/lib/math/number"; -import { FlightStar } from '@/features/flight-starfield/model/types'; +import { FlightStar } from "@/features/flight-starfield/model/types"; export const getFlightStarCount = (width: number, height: number) => { const isMobile = width < FLIGHT_STARFIELD_TUNING.mobileBreakpoint; @@ -11,7 +11,9 @@ export const getFlightStarCount = (width: number, height: number) => { const max = isMobile ? FLIGHT_STARFIELD_TUNING.starCount.mobile.max : FLIGHT_STARFIELD_TUNING.starCount.desktop.max; - const byArea = Math.round((width * height) / FLIGHT_STARFIELD_TUNING.densityDivisor); + const byArea = Math.round( + (width * height) / FLIGHT_STARFIELD_TUNING.densityDivisor, + ); return clamp(byArea, min, max); }; @@ -51,7 +53,8 @@ const createFlightSpeed = () => { }; const createFlightVisualTier = () => { - const highlight = Math.random() < FLIGHT_STARFIELD_TUNING.radius.highlightChance; + const highlight = + Math.random() < FLIGHT_STARFIELD_TUNING.radius.highlightChance; const tailRoll = Math.random(); const tailLength = tailRoll < FLIGHT_STARFIELD_TUNING.tail.pointChance @@ -95,7 +98,8 @@ const createFlightVisualTier = () => { const createFlightSpawnRadius = (width: number, height: number) => { const roll = Math.random(); const maxWideRadius = Math.min( - Math.max(width, height) * FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.maxScaleOfViewport, + Math.max(width, height) * + FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.maxScaleOfViewport, FLIGHT_STARFIELD_TUNING.spawnRadius.wideRange.maxAbsolute, ); const ringOuter = Math.min( diff --git a/src/features/flight-starfield/ui/FlightStarfieldCanvas.tsx b/src/features/flight-starfield/ui/FlightStarfieldCanvas.tsx index a87bb76..5a89a84 100644 --- a/src/features/flight-starfield/ui/FlightStarfieldCanvas.tsx +++ b/src/features/flight-starfield/ui/FlightStarfieldCanvas.tsx @@ -1,17 +1,20 @@ -'use client'; +"use client"; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef } from "react"; -import { projectFlightStar, createVanishingPoint } from '@/features/flight-starfield/lib/projection'; +import { + createVanishingPoint, + projectFlightStar, +} from "@/features/flight-starfield/lib/projection"; import { createFlightStar, createFlightVanishXJitter, getFlightStarCount, shouldRecycleFlightStar, -} from '@/features/flight-starfield/model/starfieldModel'; -import { FlightStar } from '@/features/flight-starfield/model/types'; -import { clamp } from '@/shared/lib/math/number'; -import { getPrefersReducedMotionMediaQuery } from '@/shared/lib/motion/prefersReducedMotion'; +} from "@/features/flight-starfield/model/starfieldModel"; +import { FlightStar } from "@/features/flight-starfield/model/types"; +import { clamp } from "@/shared/lib/math/number"; +import { getPrefersReducedMotionMediaQuery } from "@/shared/lib/motion/prefersReducedMotion"; export function FlightStarfieldCanvas({ vanishYOffset = -68, @@ -30,7 +33,7 @@ export function FlightStarfieldCanvas({ const canvas = canvasRef.current; if (!canvas) return; - const context = canvas.getContext('2d'); + const context = canvas.getContext("2d"); if (!context) return; let width = window.innerWidth; @@ -102,7 +105,7 @@ export function FlightStarfieldCanvas({ if (star.tailLength < 1 || movementLength < 0.001) { context.globalAlpha = visibleAlpha; - context.fillStyle = '#f8fbff'; + context.fillStyle = "#f8fbff"; context.beginPath(); context.arc(toX, toY, star.radius, 0, Math.PI * 2); context.fill(); @@ -116,7 +119,7 @@ export function FlightStarfieldCanvas({ const tailY = toY - directionY * star.tailLength; const gradient = context.createLinearGradient(tailX, tailY, toX, toY); - gradient.addColorStop(0, 'rgba(248, 251, 255, 0)'); + gradient.addColorStop(0, "rgba(248, 251, 255, 0)"); gradient.addColorStop(1, `rgba(248, 251, 255, ${visibleAlpha})`); context.strokeStyle = gradient; @@ -127,9 +130,15 @@ export function FlightStarfieldCanvas({ context.stroke(); context.globalAlpha = Math.min(1, visibleAlpha + 0.08); - context.fillStyle = '#f8fbff'; + context.fillStyle = "#f8fbff"; context.beginPath(); - context.arc(toX, toY, clamp(star.radius * 0.72, 0.6, 1.45), 0, Math.PI * 2); + context.arc( + toX, + toY, + clamp(star.radius * 0.72, 0.6, 1.45), + 0, + Math.PI * 2, + ); context.fill(); context.globalAlpha = 1; }; @@ -144,9 +153,9 @@ export function FlightStarfieldCanvas({ vp.y, centerProtectRadius * 1.35, ); - veil.addColorStop(0, 'rgba(160, 185, 235, 0.08)'); - veil.addColorStop(0.55, 'rgba(90, 114, 170, 0.03)'); - veil.addColorStop(1, 'rgba(0, 0, 0, 0)'); + veil.addColorStop(0, "rgba(160, 185, 235, 0.08)"); + veil.addColorStop(0.55, "rgba(90, 114, 170, 0.03)"); + veil.addColorStop(1, "rgba(0, 0, 0, 0)"); context.fillStyle = veil; context.fillRect(0, 0, width, height); @@ -161,14 +170,14 @@ export function FlightStarfieldCanvas({ height / 2, Math.max(width, height) * 0.95, ); - vignette.addColorStop(0, 'rgba(0, 0, 0, 0)'); - vignette.addColorStop(1, 'rgba(0, 0, 0, 0.82)'); + vignette.addColorStop(0, "rgba(0, 0, 0, 0)"); + vignette.addColorStop(1, "rgba(0, 0, 0, 0.82)"); context.fillStyle = vignette; context.fillRect(0, 0, width, height); }; const drawFrame = (moveStars: boolean) => { - context.fillStyle = 'rgba(2, 5, 10, 0.3)'; + context.fillStyle = "rgba(2, 5, 10, 0.3)"; context.fillRect(0, 0, width, height); drawCenterVeil(); @@ -254,8 +263,8 @@ export function FlightStarfieldCanvas({ } }; - window.addEventListener('resize', handleResize); - motionQuery.addEventListener('change', handleMotionChange); + window.addEventListener("resize", handleResize); + motionQuery.addEventListener("change", handleMotionChange); if (prefersReducedMotion || isPaused) { renderStatic(); @@ -264,11 +273,16 @@ export function FlightStarfieldCanvas({ } return () => { - window.removeEventListener('resize', handleResize); - motionQuery.removeEventListener('change', handleMotionChange); + window.removeEventListener("resize", handleResize); + motionQuery.removeEventListener("change", handleMotionChange); stopAnimation(); }; }, [vanishYOffset, centerProtectRadius, isPaused]); - return ; + return ( + + ); } diff --git a/src/shared/config/i18n.ts b/src/shared/config/i18n.ts index a3dcd73..1acce80 100644 --- a/src/shared/config/i18n.ts +++ b/src/shared/config/i18n.ts @@ -82,11 +82,12 @@ const enMessages = { "status.reoriented": "Reoriented", "status.aborted": "Aborted Early", "status.in_progress": "In Progress", -} as const; +} satisfies Record; -type I18nMessages = typeof enMessages; +type MessageKey = keyof typeof enMessages; +type I18nMessages = Record; -const koMessages: I18nMessages = { +const koMessages = { "layout.nav.log": "항해일지", "layout.nav.settings": "설정", "layout.nav.language": "언어", @@ -155,9 +156,9 @@ const koMessages: I18nMessages = { "status.reoriented": "🧭 방향 재설정", "status.aborted": "🚨 조기 귀환", "status.in_progress": "진행 중", -}; +} satisfies I18nMessages; -const jaMessages: I18nMessages = { +const jaMessages = { "layout.nav.log": "航海ログ", "layout.nav.settings": "設定", "layout.nav.language": "言語", @@ -226,15 +227,17 @@ const jaMessages: I18nMessages = { "status.reoriented": "🧭 方針再設定", "status.aborted": "🚨 早期帰還", "status.in_progress": "進行中", -}; +} satisfies I18nMessages; + +const normalizedEnMessages: I18nMessages = enMessages; export const I18N_MESSAGES: Record = { ko: koMessages, - en: enMessages, + en: normalizedEnMessages, ja: jaMessages, }; -export type I18nKey = keyof I18nMessages; +export type I18nKey = MessageKey; export type TranslationParams = Record;