diff --git a/apps/web/src/App.module.css b/apps/web/src/App.module.css deleted file mode 100644 index 3afd79f..0000000 --- a/apps/web/src/App.module.css +++ /dev/null @@ -1,80 +0,0 @@ -/* アプリ全体 */ -.app { - background-color: #f5e6d3; - height: 100vh; - width: 100vw; - margin: 0; - padding: 0; -} - -.screen { - width: 100%; - height: 100%; - border-radius: 36px; - overflow: hidden; - position: relative; -} - -.phone-container { - width: 375px; - height: 812px; - margin: 20px auto; - background: #2c2c2c; - border-radius: 40px; - padding: 4px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - position: relative; - overflow-y: hidden; - overflow-x: hidden; -} - -@media (max-width: 460px) { - .phone-container { - width: 100vw; - height: 100%; - margin: 0; - border-radius: 0; - } - - .screen { - border-radius: 0; - } -} - -/* テスト用の結果画面 */ -.testResult { - padding: 20px; - text-align: center; - background-color: #f5e6d3; - height: 100vh; - display: flex; - flex-direction: column; - justify-content: center; -} - -.testResultTitle { - font-size: 24px; - margin-bottom: 20px; - font-family: 'Courier New', monospace; -} - -.testResultContent { - margin-bottom: 30px; -} - -.testResultButton { - padding: 15px 30px; - background-color: #6b46c1; - color: white; - border: 3px solid #4c1d95; - border-radius: 0; - font-size: 16px; - font-family: 'Courier New', monospace; - cursor: pointer; - box-shadow: 4px 4px 0px #2d1b69; -} - -.testResultButton:active { - transform: translate(2px, 2px); - box-shadow: 2px 2px 0px #2d1b69; -} diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 770b6ce..9171404 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,70 +1,10 @@ -// import { useState } from 'react'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; - -import { AnimatePresence } from 'framer-motion'; - -import styles from './App.module.css'; -import { HomeScreen } from './components/home/HomeScreen'; -import { ModeScreen } from './components/mode/ModeScreen'; -import { PhotoPreview } from './components/photo/PhotoPreview'; -import { PhotoScreen } from './components/photo/PhotoScreen'; -// import { ShootingScreen } from './components/game/shooting-screen'; -// import type { JudgeResult, Theme } from './components/game/types'; -import { ResultScreen } from './components/result/ResultScreen'; +import { AppRoutes } from '@/web/AppRoutes'; +import styles from '@/web/styles/App.module.css'; function App() { - // // テスト用のお題データ - // const testTheme: Theme = { - // id: 1, - // difficulty: 'NORMAL', - // theme: 'テスト', // 実際は1・2枚目から渡される - // aiCondition: { label: 'Test' }, - // }; - - // const [showShooting, setShowShooting] = useState(true); - // const [result, setResult] = useState(null); - - // const handleComplete = (judgeResult: JudgeResult) => { - // setResult(judgeResult); - // setShowShooting(false); - // console.log('撮影完了:', judgeResult); - // // 実際は4枚目(結果画面)に遷移 - // }; - - // const handleRetry = () => { - // setResult(null); - // setShowShooting(true); - // }; - return (
- - - - } /> - } /> - } /> - } /> - } /> - - - - {/* {showShooting ? ( - - ) : ( -
-

テスト完了

- {result && ( -
-

スコア: {(result.label_score * 100).toFixed(1)}%

-

結果: {result.success ? '成功' : '失敗'}

-
- )} - -
- )} */} +
); } diff --git a/apps/web/src/AppRoutes.tsx b/apps/web/src/AppRoutes.tsx new file mode 100644 index 0000000..e6a61ac --- /dev/null +++ b/apps/web/src/AppRoutes.tsx @@ -0,0 +1,34 @@ +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; + +import { HomeScreen } from '@/web/components/home/HomeScreen'; +import { ModeScreen } from '@/web/components/mode/ModeScreen'; +import { PhotoPreview } from '@/web/components/photo/PhotoPreview'; +import { PhotoScreen } from '@/web/components/photo/PhotoScreen'; +import { ResultScreen } from '@/web/components/result/ResultScreen'; + +const router = createBrowserRouter([ + { + path: '/', + element: , + }, + { + path: '/mode', + element: , + }, + { + path: '/photo', + element: , + }, + { + path: '/photo/preview', + element: , + }, + { + path: '/result', + element: , + }, +]); + +export const AppRoutes = () => { + return ; +}; diff --git a/apps/web/src/components/camera/camera.tsx b/apps/web/src/components/camera/camera.tsx index 0d1ba7a..d69e61a 100644 --- a/apps/web/src/components/camera/camera.tsx +++ b/apps/web/src/components/camera/camera.tsx @@ -7,7 +7,13 @@ import { } from 'react'; import styles from './camera.module.css'; -import type { CameraProps, CameraRef } from './types'; + +import type { CameraRef } from '@/web/types'; + +interface CameraProps { + /** 撮影完了時のコールバック関数(base64形式の画像データを受け取る) */ + onCapture: (imageData: string) => void; +} /** * カメラプレビューと撮影機能を提供するコンポーネント diff --git a/apps/web/src/components/camera/index.tsx b/apps/web/src/components/camera/index.tsx deleted file mode 100644 index 6307949..0000000 --- a/apps/web/src/components/camera/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -/** - * カメラコンポーネントのメインエクスポート - */ -export { Camera } from './camera'; -export type { CameraProps, CameraState } from './types'; diff --git a/apps/web/src/components/camera/types.ts b/apps/web/src/components/camera/types.ts deleted file mode 100644 index f794d68..0000000 --- a/apps/web/src/components/camera/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * カメラコンポーネント関連の型定義 - */ - -/** - * カメラコンポーネントのProps - */ -export interface CameraProps { - /** 撮影完了時のコールバック関数(base64形式の画像データを受け取る) */ - onCapture: (imageData: string) => void; -} - -/** - * カメラの状態 - */ -export interface CameraState { - /** ローディング状態 */ - isLoading: boolean; - /** エラーメッセージ */ - error: string | null; -} - -/** - * カメラコンポーネントのRef(外部から撮影をトリガー) - */ -export interface CameraRef { - capture: () => void; -} diff --git a/apps/web/src/components/game/shooting-screen.module.css b/apps/web/src/components/game/shooting-screen.module.css deleted file mode 100644 index d52e2e7..0000000 --- a/apps/web/src/components/game/shooting-screen.module.css +++ /dev/null @@ -1,206 +0,0 @@ -/* 全体のコンテナ */ -.shootingScreen { - height: 100vh; - width: 100vw; - display: flex; - flex-direction: column; - background-color: #f5e6d3; - font-family: 'Courier New', monospace; - image-rendering: pixelated; - margin: 0; - padding: 0; -} - -/* メインコンテンツエリア */ -.mainContent { - flex: 1; - overflow: hidden; - position: relative; - background-color: #f5e6d3; -} - -/* 撮影画面 */ -.shootingArea { - height: 100%; - width: 100%; - position: relative; - background-color: #f5e6d3; -} - -/* お題表示(吹き出し)- 「お題を撮影しよう」用 */ -.themeDisplay { - position: absolute; - top: 30px; - left: 50%; - transform: translateX(-50%); - z-index: 10; - background-color: #ffffff; - color: #333333; - padding: 12px 25px; - border: 3px solid #000000; - border-radius: 20px; - text-align: center; - font-family: 'Courier New', monospace; - font-size: 16px; - font-weight: bold; - box-shadow: 4px 4px 0px #cccccc; -} - -/* お題内容表示 - 「お題:○○」用 */ -.themeContent { - position: absolute; - top: 100px; /* 吹き出しの下に配置 */ - left: 50%; - transform: translateX(-50%); - z-index: 9; - background-color: #e8d5b7; /* 少し違う色 */ - color: #6b46c1; - padding: 15px 30px; - border: 3px solid #000000; - border-radius: 15px; - text-align: center; - font-family: 'Courier New', monospace; - font-size: 20px; - font-weight: bold; - box-shadow: 3px 3px 0px #cccccc; -} - -/* お題テキスト */ -.themeText { - font-size: 20px; - color: #6b46c1; - margin-top: 8px; -} - -/* キャラクター表示エリア - 位置調整 */ -.characterArea { - position: absolute; - top: 180px; /* 下に移動 */ - left: 50%; - transform: translateX(-50%); - z-index: 5; -} - -/* キャラクター(仮) */ -.character { - width: 80px; - height: 80px; - background-color: #4a90e2; - border: 2px solid #000000; - image-rendering: pixelated; -} - -/* タイマー表示エリア - 位置調整 */ -.timerArea { - position: absolute; - top: 280px; /* 下に移動 */ - left: 50%; - transform: translateX(-50%); - display: flex; - align-items: center; - background-color: #8b4513; - color: #f5e6d3; - padding: 10px 20px; - border: 2px solid #654321; - font-family: 'Courier New', monospace; - font-size: 16px; - font-weight: bold; -} - -/* カメラプレビューエリア - 位置調整 */ -.cameraPreviewArea { - position: absolute; - top: 340px; /* さらに下に移動 */ - left: 50%; - transform: translateX(-50%); - width: 280px; - height: 210px; - border: 3px solid #000000; - border-radius: 8px; - overflow: hidden; - background-color: #000000; - z-index: 3; -} - -/* カメラ表示(非表示から表示に変更) */ -.cameraDisplay { - width: 100%; - height: 100%; -} - -/* 撮影ボタンエリアの位置調整 */ -.captureButtonArea { - position: absolute; - bottom: 30px; /* 少し上に移動 */ - left: 50%; - transform: translateX(-50%); -} - -/* 撮影ボタン */ -.captureButton { - padding: 15px 40px; - background-color: #6b46c1; - color: #ffffff; - border: 3px solid #4c1d95; - border-radius: 0; - font-family: 'Courier New', monospace; - font-size: 18px; - font-weight: bold; - cursor: pointer; - box-shadow: 4px 4px 0px #2d1b69; - transition: all 0.1s; -} - -.captureButton:active { - transform: translate(2px, 2px); - box-shadow: 2px 2px 0px #2d1b69; -} - -/* カメラ非表示 */ -.hiddenCamera { - display: none; -} - -/* 判定中画面 */ -.judgingArea { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - padding: 20px; - background-color: #f5e6d3; -} - -/* 撮影画像表示 */ -.capturedImageFrame { - border: 4px solid #000000; - background-color: #ffffff; - padding: 10px; - margin-bottom: 30px; -} - -.capturedImage { - width: 200px; - height: 200px; - object-fit: cover; - image-rendering: pixelated; -} - -/* 判定中テキスト */ -.judgingTitle { - font-size: 24px; - font-weight: bold; - margin-bottom: 20px; - font-family: 'Courier New', monospace; - color: #4a4a4a; - text-align: center; -} - -.judgingDescription { - font-size: 16px; - color: #666; - text-align: center; - font-family: 'Courier New', monospace; - line-height: 1.6; -} diff --git a/apps/web/src/components/game/shooting-screen.tsx b/apps/web/src/components/game/shooting-screen.tsx deleted file mode 100644 index 42b8c34..0000000 --- a/apps/web/src/components/game/shooting-screen.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useRef, useState } from 'react'; - -import { Camera } from '../camera'; -import type { CameraRef } from '../camera/types'; -import styles from './shooting-screen.module.css'; -import { Timer } from './timer'; -import type { JudgeResult, Theme } from './types'; - -/** - * 3枚目:撮影画面専用コンポーネント - * - * @description - * - お題を受け取って撮影画面を表示 - * - 1分間のタイマー付き撮影 - * - AI判定後に結果を返す - */ - -interface ShootingScreenProps { - /** 撮影するお題(1・2枚目から受け取る) */ - theme: Theme; - /** 撮影・判定完了時のコールバック */ - onComplete: (result: JudgeResult) => void; - /** キャンセル時のコールバック */ - onCancel?: () => void; -} - -export const ShootingScreen: React.FC = ({ - theme, - onComplete, -}) => { - const [phase, setPhase] = useState<'SHOOTING' | 'JUDGING'>('SHOOTING'); - const [capturedImage, setCapturedImage] = useState(null); - const cameraRef = useRef(null); - - // 撮影完了時の処理 - const handleImageCapture = async (imageData: string) => { - setCapturedImage(imageData); - setPhase('JUDGING'); - - // AI判定の実行 - await sendImageToAPI(imageData); - }; - - // 時間切れ時の処理 - const handleTimeUp = () => { - console.log('⏰ 時間切れ!'); - // 時間切れ時は自動で撮影を実行 - handleManualCapture(); - }; - - // API送信処理 - const sendImageToAPI = async (imageData: string) => { - try { - const apiUrl = - import.meta.env.VITE_API_URL_DEV || 'http://localhost:8787'; - - const response = await fetch(`${apiUrl}/judge`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - imageData: imageData, - theme: theme.theme, - }), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - // 結果を親コンポーネントに返す - onComplete(result); - } catch (error) { - console.error('API送信エラー:', error); - const errorResult: JudgeResult = { - success: false, - theme: theme.theme, - label_score: 0, - detected_labels: [], - message: '', - error: '画像の送信に失敗しました。', - }; - - onComplete(errorResult); - } - }; - - // 手動撮影処理 - const handleManualCapture = () => { - if (cameraRef.current?.capture) { - cameraRef.current.capture(); - } - }; - - return ( -
-
- {phase === 'SHOOTING' && ( -
- {/* お題を撮影しよう - 吹き出し */} -
お題を撮影しよう
- - {/* お題内容 - 分離された表示 */} -
お題:{theme.theme}
- - {/* キャラクター */} -
-
-
- - {/* タイマー */} -
- -
- - {/* カメラプレビュー */} -
- -
- - {/* 撮影ボタン */} -
- -
-
- )} - - {phase === 'JUDGING' && ( -
- {capturedImage && ( -
- 撮影された画像 -
- )} - -
🤖 AI が評価中...
- -
- お題「{theme.theme}」を判定しています -
- しばらくお待ちください -
-
- )} -
-
- ); -}; diff --git a/apps/web/src/components/game/timer.module.css b/apps/web/src/components/game/timer.module.css deleted file mode 100644 index 3ca02b4..0000000 --- a/apps/web/src/components/game/timer.module.css +++ /dev/null @@ -1,41 +0,0 @@ -/* タイマーコンテナ */ -.timerContainer { - display: flex; - align-items: center; - font-family: 'Courier New', monospace; - font-size: 16px; - font-weight: bold; -} - -/* タイマーアイコン */ -.timerIcon { - margin-right: 8px; -} - -/* タイマーテキスト */ -.timerText { - /* 色は動的に設定 */ -} - -/* 緊急時の点滅 */ -.timerUrgent { - animation: blink 1s infinite; -} - -@keyframes blink { - 0%, - 50% { - opacity: 1; - } - 51%, - 100% { - opacity: 0.3; - } -} - -/* 緊急メッセージ */ -.urgentMessage { - font-size: 10px; - color: #ff4444; - margin-top: 2px; -} diff --git a/apps/web/src/components/game/timer.tsx b/apps/web/src/components/game/timer.tsx deleted file mode 100644 index 823e5b5..0000000 --- a/apps/web/src/components/game/timer.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useEffect, useState } from 'react'; - -import styles from './timer.module.css'; -import type { TimerProps } from './types'; - -/** - * ゲーム用カウントダウンタイマーコンポーネント - * - * @description - * - 1分間(60秒)のカウントダウンタイマー - * - 残り時間が少なくなると色が変化 - * - 時間切れ時に自動的にコールバックを実行 - * - * @example - * ```tsx - * console.log('時間切れ!')} - * isActive={true} - * /> - * ``` - */ -export const Timer: React.FC = ({ - initialTime, - onTimeUp, - isActive, - resetKey = 0, -}) => { - const [timeLeft, setTimeLeft] = useState(initialTime); - - // タイマーリセット処理 - useEffect(() => { - setTimeLeft(initialTime); - }, [initialTime, resetKey]); - - // カウントダウン処理 - useEffect(() => { - if (!isActive) return; - - if (timeLeft <= 0) { - onTimeUp(); - return; - } - - const timer = setInterval(() => { - setTimeLeft((prev) => { - if (prev <= 1) { - onTimeUp(); - return 0; - } - return prev - 1; - }); - }, 1000); - - return () => clearInterval(timer); - }, [timeLeft, isActive, onTimeUp]); - - // 残り時間に応じた色の決定 - const getTimerColor = () => { - if (timeLeft <= 10) return '#ff4444'; - if (timeLeft <= 30) return '#ff8800'; - return '#F5E6D3'; - }; - - // 時間を MM:SS 形式でフォーマット - const formatTime = (seconds: number) => { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; - }; - - return ( -
- - - 制限時間 {formatTime(timeLeft)} - - {timeLeft <= 10 &&
急いで!
} -
- ); -}; diff --git a/apps/web/src/components/game/types.ts b/apps/web/src/components/game/types.ts deleted file mode 100644 index ccaa868..0000000 --- a/apps/web/src/components/game/types.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 3枚目撮影画面用の型定義 - */ - -/** - * お題の型定義(データベースと同じ構造) - */ -export interface Theme { - /** お題ID */ - id: number; - /** 難易度レベル */ - difficulty: 'EASY' | 'NORMAL' | 'HARD'; - /** お題テキスト */ - theme: string; - /** AI判定条件 */ - aiCondition: unknown; -} - -/** - * AI判定結果の型定義 - */ -export interface JudgeResult { - success: boolean; - theme: string; - label_score: number; - detected_labels: Array<{ - description: string; - score: number; - }>; - image_properties?: unknown; - message: string; - error?: string; -} - -/** - * タイマーコンポーネントのProps - */ -export interface TimerProps { - /** 初期時間(秒) */ - initialTime: number; - /** タイマー終了時のコールバック */ - onTimeUp: () => void; - /** タイマー開始フラグ */ - isActive: boolean; - /** タイマーリセット用のキー */ - resetKey?: number; -} diff --git a/apps/web/src/components/home/HomeScreen.tsx b/apps/web/src/components/home/HomeScreen.tsx index 493b448..834895a 100644 --- a/apps/web/src/components/home/HomeScreen.tsx +++ b/apps/web/src/components/home/HomeScreen.tsx @@ -4,10 +4,9 @@ import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import { motion } from 'framer-motion'; -import huntoru from '../../assets/huntoru.png'; -import styles from './HomeScreen.module.css'; - -import components from '@/web/App.module.css'; +import huntoru from '@/web/assets/huntoru.png'; +import styles from '@/web/components/home/HomeScreen.module.css'; +import components from '@/web/styles/App.module.css'; export const HomeScreen = () => { const navigate = useNavigate(); diff --git a/apps/web/src/components/index.tsx b/apps/web/src/components/index.tsx deleted file mode 100644 index 1f87be7..0000000 --- a/apps/web/src/components/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/** - * 全コンポーネントのエクスポート - */ -// eslint-disable-next-line react-refresh/only-export-components -export * from './camera'; -export { ShootingScreen } from './game/shooting-screen'; -export { Timer } from './game/timer'; -export type { JudgeResult, Theme, TimerProps } from './game/types'; diff --git a/apps/web/src/components/mode/ModeScreen.tsx b/apps/web/src/components/mode/ModeScreen.tsx index 33781ec..f63ba57 100644 --- a/apps/web/src/components/mode/ModeScreen.tsx +++ b/apps/web/src/components/mode/ModeScreen.tsx @@ -4,12 +4,11 @@ import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import { motion } from 'framer-motion'; -import huntoru from '../../assets/huntoru.png'; -import { PixelBubble } from '../ui/PixelBubble'; -import styles from './ModeScreen.module.css'; - -import components from '@/web/App.module.css'; +import huntoru from '@/web/assets/huntoru.png'; +import styles from '@/web/components/mode/ModeScreen.module.css'; +import { PixelBubble } from '@/web/components/ui/PixelBubble'; import { API_CONFIG } from '@/web/lib/api'; +import components from '@/web/styles/App.module.css'; import type { Theme } from '@/web/types'; export const ModeScreen = () => { diff --git a/apps/web/src/components/photo/PhotoPreview.tsx b/apps/web/src/components/photo/PhotoPreview.tsx index 7a7c2b3..aa0d310 100644 --- a/apps/web/src/components/photo/PhotoPreview.tsx +++ b/apps/web/src/components/photo/PhotoPreview.tsx @@ -4,10 +4,9 @@ import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import { motion } from 'framer-motion'; -import styles from './PhotoPreview.module.css'; - -import components from '@/web/App.module.css'; +import styles from '@/web/components/photo/PhotoPreview.module.css'; import { API_CONFIG } from '@/web/lib/api'; +import components from '@/web/styles/App.module.css'; interface PhotoPreviewProps { onConfirm?: () => void; diff --git a/apps/web/src/components/photo/PhotoScreen.tsx b/apps/web/src/components/photo/PhotoScreen.tsx index afa4f72..2a3009f 100644 --- a/apps/web/src/components/photo/PhotoScreen.tsx +++ b/apps/web/src/components/photo/PhotoScreen.tsx @@ -5,12 +5,10 @@ import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import { motion } from 'framer-motion'; -import { Camera } from '../camera'; -import styles from './PhotoScreen.module.css'; - -import components from '@/web/App.module.css'; -import type { CameraRef } from '@/web/components/camera/types'; -import type { Theme } from '@/web/types'; +import { Camera } from '@/web/components/camera/camera'; +import styles from '@/web/components/photo/PhotoScreen.module.css'; +import components from '@/web/styles/App.module.css'; +import type { CameraRef, Theme } from '@/web/types'; /** * フォーカスリングの位置情報を表す型 diff --git a/apps/web/src/components/result/ResultScreen.tsx b/apps/web/src/components/result/ResultScreen.tsx index 0503b11..fffbed5 100644 --- a/apps/web/src/components/result/ResultScreen.tsx +++ b/apps/web/src/components/result/ResultScreen.tsx @@ -4,12 +4,11 @@ import { useNavigate } from 'react-router-dom'; import clsx from 'clsx'; import { motion } from 'framer-motion'; -import { PixelBubble } from '../ui/PixelBubble'; -import styles from './ResultScreen.module.css'; - -import components from '@/web/App.module.css'; import huntoru_angry from '@/web/assets/huntoru_angry.gif'; import huntoru_happy from '@/web/assets/huntoru_happy.gif'; +import styles from '@/web/components/result/ResultScreen.module.css'; +import { PixelBubble } from '@/web/components/ui/PixelBubble'; +import components from '@/web/styles/App.module.css'; export const ResultScreen = () => { const progressBarRef = useRef(null); @@ -118,13 +117,6 @@ export const ResultScreen = () => {
{/* 上部の情報 */}
- {/*
- {isMatch ? '🎉' : '😭'} -
-

- {isMatch ? '成功!' : '失敗...'} -

*/} - {/* プログレスバースコア表示 */}
diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 4a78bd1..a880fa4 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -1,9 +1,8 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; -import App from './App.tsx'; - -import './index.css'; +import '@/web/styles/index.css'; +import App from '@/web/App.tsx'; createRoot(document.getElementById('root')!).render( diff --git a/apps/web/src/styles/App.module.css b/apps/web/src/styles/App.module.css new file mode 100644 index 0000000..720a2cb --- /dev/null +++ b/apps/web/src/styles/App.module.css @@ -0,0 +1,33 @@ +.screen { + width: 100%; + height: 100%; + border-radius: 36px; + overflow: hidden; + position: relative; +} + +.phone-container { + width: 375px; + height: 812px; + margin: 20px auto; + background: #2c2c2c; + border-radius: 40px; + padding: 4px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + position: relative; + overflow-y: hidden; + overflow-x: hidden; +} + +@media (max-width: 460px) { + .phone-container { + width: 100vw; + height: 100%; + margin: 0; + border-radius: 0; + } + + .screen { + border-radius: 0; + } +} diff --git a/apps/web/src/index.css b/apps/web/src/styles/index.css similarity index 88% rename from apps/web/src/index.css rename to apps/web/src/styles/index.css index a094bd6..2426d27 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/styles/index.css @@ -57,10 +57,3 @@ body, font-weight: 400; font-style: normal; } - -/* ピクセルアート風の画像レンダリング */ -.pixelated { - image-rendering: pixelated; - image-rendering: -moz-crisp-edges; - image-rendering: crisp-edges; -} diff --git a/apps/web/src/types/index.ts b/apps/web/src/types/index.ts index 6061c84..e307477 100644 --- a/apps/web/src/types/index.ts +++ b/apps/web/src/types/index.ts @@ -3,3 +3,10 @@ export interface Theme { difficulty: 'EASY' | 'NORMAL' | 'HARD'; theme: string; } + +/** + * カメラコンポーネントのRef(外部から撮影をトリガー) + */ +export interface CameraRef { + capture: () => void; +} diff --git a/apps/web/wrangler.jsonc b/apps/web/wrangler.jsonc index f090664..9c1b87a 100644 --- a/apps/web/wrangler.jsonc +++ b/apps/web/wrangler.jsonc @@ -3,8 +3,4 @@ "name": "huntoru-web", "compatibility_date": "2025-07-18", "pages_build_output_dir": "dist", - "vars": { - "API_URL_DEV": "http://localhost:8787", - "API_URL_PROD": "https://huntoru-api.nka21dev.workers.dev/", - }, }