diff --git a/.biome.json b/.biome.json
new file mode 100644
index 0000000..5b87575
--- /dev/null
+++ b/.biome.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
+ "organizeImports": { "enabled": true },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true,
+ "correctness": { "useExhaustiveDependencies": "error" },
+ "suspicious": { "noExplicitAny": "error" }
+ }
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2,
+ "lineWidth": 80
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "double",
+ "semicolons": "always"
+ }
+ }
+}
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..e77713f
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,4 @@
+// ESLint の設定ファイル
+module.exports = {
+ extends: ['next/core-web-vitals'],
+};
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..3fd0b67
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,40 @@
+name: CI
+
+on:
+ push:
+ branches: [main, develop]
+ pull_request:
+ branches: [main, develop]
+
+jobs:
+ lint-and-typecheck:
+ name: Lint and Type Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ - name: Install dependencies
+ run: npm ci
+ - name: Run Biome Lint & Format Check
+ run: npm run lint:biome
+ - name: Run TypeScript Type Check
+ run: npm run build
+ build:
+ name: Build Project
+ runs-on: ubuntu-latest
+ needs: lint-and-typecheck
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ - name: Install dependencies
+ run: npm ci
+ - name: Build Next.js application
+ run: npm run build
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..2e5efde
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,30 @@
+name: Deploy to Cloudflare Pages
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ publish:
+ name: Publish to Cloudflare Pages
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ deployments: write
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ - name: Install dependencies
+ run: npm ci
+ - name: Build with OpenNext
+ run: npm run opennext:build
+ - name: Publish to Cloudflare Pages
+ uses: cloudflare/wrangler-action@v3
+ with:
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ projectName: "prototyping-base-app-router"
diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh
new file mode 100755
index 0000000..af8e43c
--- /dev/null
+++ b/.husky/_/husky.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+# Husky による Git フックの共通処理
+export PATH="$PATH:$(npm bin)"
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..0499f72
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,5 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npx biome check --apply .
+npx biome format --write .
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..8685eda
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,35 @@
+# LLM開発ルール
+
+## LLMで生成したいこと
+- UI生成
+- 機能実装
+- デプロイ
+
+## 絶対に守るべきこと
+- シンプルなアーキテクチャ
+- 日本語でのドキュメント・コードコメント
+- 最新バージョン技術の使用
+- 秘匿情報を含まない公開リポジトリ
+- 汎用的な記述
+
+## 検討事項
+- 技術スタック
+- DB
+- UIデザイン
+- デプロイ先
+
+## リポジトリに必要なもの
+- LLM用rulesファイル (このファイル)
+- packages.json
+- README.md
+- Linter設定
+- Formatter設定
+- Typescript設定
+- TailwindCSS設定
+- envファイル
+- CloudflareでのNext.jsアプリ作成方法
+
+## 不要なもの
+- サンプルアプリケーション
+- 具体的な実装
+- ハッカソン情報
diff --git a/README.md b/README.md
index ab51738..012dd85 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,10 @@
## 技術スタック
-- **フロントエンド**: Next.js, React
-- **スタイリング**: CSS Modules または styled-components
-- **バックエンド**: API routes (Next.jsのサーバレスAPI機能を使用)
+- **フロントエンド**: Next.js(App Router) と React
+- **スタイリング**: TailwindCSS
+- **言語/ツール**: TypeScript, Biome, Husky
+- **デプロイ**: OpenNext を利用した Cloudflare Pages へのデプロイを想定
## プロジェクト構成
@@ -44,3 +45,10 @@
```bash
npm install
npm run dev
+```
+
+アプリを静的にエクスポートする場合は以下を実行してください:
+
+```bash
+npm run opennext:build
+```
diff --git a/components/Alert.js b/components/Alert.js
deleted file mode 100644
index 47dec6e..0000000
--- a/components/Alert.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-
-const Alert = ({ message, isVisible }) => {
- if (!isVisible) return null;
-
- return (
-
- );
-};
-
-export default Alert;
diff --git a/components/SettingsForm.js b/components/SettingsForm.js
deleted file mode 100644
index e4db371..0000000
--- a/components/SettingsForm.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React, { useState } from 'react';
-
-const SettingsForm = ({ onSave }) => {
- const [timerSettings, setTimerSettings] = useState({ total: 0, section: 0 });
-
- const handleSubmit = (event) => {
- event.preventDefault();
- onSave(timerSettings);
- };
-
- return (
-
- );
-};
-
-export default SettingsForm;
diff --git a/components/Timer.js b/components/Timer.js
deleted file mode 100644
index cd642fc..0000000
--- a/components/Timer.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React, { useState, useEffect } from 'react';
-
-const Timer = ({ initialTime }) => {
- const [timeLeft, setTimeLeft] = useState(initialTime);
-
- useEffect(() => {
- if (timeLeft > 0) {
- const timerId = setTimeout(() => setTimeLeft(timeLeft - 1), 1000);
- return () => clearTimeout(timerId);
- }
- }, [timeLeft]);
-
- return (
-
-
{timeLeft}
-
- );
-};
-
-export default Timer;
diff --git a/next-env.d.ts b/next-env.d.ts
new file mode 100644
index 0000000..de7cb05
--- /dev/null
+++ b/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: このファイルは自動生成されます。
+
diff --git a/next.config.js b/next.config.js
new file mode 100644
index 0000000..15c0849
--- /dev/null
+++ b/next.config.js
@@ -0,0 +1,7 @@
+// Next.js の設定ファイル
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+};
+
+module.exports = nextConfig;
diff --git a/package.json b/package.json
index 7f5dcdb..6f61c3e 100644
--- a/package.json
+++ b/package.json
@@ -1,16 +1,37 @@
{
- "name": "interview-timer-app",
- "version": "1.0.0",
- "description": "A timer application for managing interview sessions",
- "main": "index.js",
- "scripts": {
- "dev": "next dev",
- "build": "next build",
- "start": "next start"
- },
- "dependencies": {
- "next": "latest",
- "react": "latest",
- "react-dom": "latest"
- }
+ "name": "prototyping-base-app-router",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint",
+ "lint:biome": "biome check --apply .",
+ "format:biome": "biome format --write .",
+ "prepare": "husky install",
+ "cf-typegen": "wrangler types --env cloudflare-pages > src/types/cloudflare.d.ts",
+ "opennext:build": "opennext build"
+ },
+ "dependencies": {
+ "next": "latest",
+ "react": "latest",
+ "react-dom": "latest"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "latest",
+ "@cloudflare/workers-types": "latest",
+ "@types/node": "latest",
+ "@types/react": "latest",
+ "@types/react-dom": "latest",
+ "autoprefixer": "latest",
+ "eslint": "latest",
+ "eslint-config-next": "latest",
+ "husky": "latest",
+ "opennext": "latest",
+ "postcss": "latest",
+ "tailwindcss": "latest",
+ "typescript": "latest",
+ "wrangler": "latest"
+ }
}
diff --git a/pages/_app.js b/pages/_app.js
deleted file mode 100644
index 5f55999..0000000
--- a/pages/_app.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// pages/_app.js
-import '../styles/globals.css'
-
-function MyApp({ Component, pageProps }) {
- return
-}
-
-export default MyApp;
diff --git a/pages/index.js b/pages/index.js
deleted file mode 100644
index 1e15f1c..0000000
--- a/pages/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import Head from 'next/head';
-import Timer from '../components/Timer';
-
-export default function Home() {
- return (
-
-
-
Interview Timer
-
-
-
-
-
- Welcome to the Interview Timer
-
-
-
-
-
- );
-}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..0912b86
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,7 @@
+// PostCSS の設定ファイル
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 0000000..49a46bf
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,4 @@
+/* TailwindCSS の基本スタイルを読み込み */
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..de87420
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,11 @@
+import '../app/globals.css';
+import type { ReactNode } from 'react';
+
+// ルートレイアウト
+export default function RootLayout({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..d03c5f3
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,11 @@
+import Timer from '../components/Timer';
+
+// トップページ
+export default function Page() {
+ return (
+
+ Interview Timer App
+
+
+ );
+}
diff --git a/src/components/Alert.tsx b/src/components/Alert.tsx
new file mode 100644
index 0000000..54ff4fe
--- /dev/null
+++ b/src/components/Alert.tsx
@@ -0,0 +1,9 @@
+// アラート表示用コンポーネント
+export default function Alert({ message, isVisible }: { message: string; isVisible: boolean }) {
+ if (!isVisible) return null;
+ return (
+
+ );
+}
diff --git a/src/components/SettingsForm.tsx b/src/components/SettingsForm.tsx
new file mode 100644
index 0000000..863d74c
--- /dev/null
+++ b/src/components/SettingsForm.tsx
@@ -0,0 +1,38 @@
+import { useState, FormEvent } from 'react';
+
+interface Settings {
+ total: number;
+ section: number;
+}
+
+// タイマー設定フォーム
+export default function SettingsForm({ onSave }: { onSave: (settings: Settings) => void }) {
+ const [timerSettings, setTimerSettings] = useState({ total: 0, section: 0 });
+
+ const handleSubmit = (event: FormEvent) => {
+ event.preventDefault();
+ onSave(timerSettings);
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/Timer.tsx b/src/components/Timer.tsx
new file mode 100644
index 0000000..e5f5653
--- /dev/null
+++ b/src/components/Timer.tsx
@@ -0,0 +1,19 @@
+import { useState, useEffect } from 'react';
+
+// シンプルなタイマーコンポーネント
+export default function Timer({ initialTime }: { initialTime: number }) {
+ const [timeLeft, setTimeLeft] = useState(initialTime);
+
+ useEffect(() => {
+ if (timeLeft > 0) {
+ const timerId = setTimeout(() => setTimeLeft(timeLeft - 1), 1000);
+ return () => clearTimeout(timerId);
+ }
+ }, [timeLeft]);
+
+ return (
+
+
{timeLeft}
+
+ );
+}
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 0000000..f43b90c
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,8 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+/* 必要に応じてグローバルスタイルを追加 */
+body {
+ font-family: sans-serif;
+}
diff --git a/src/types/cloudflare.d.ts b/src/types/cloudflare.d.ts
new file mode 100644
index 0000000..ea18a73
--- /dev/null
+++ b/src/types/cloudflare.d.ts
@@ -0,0 +1,3 @@
+// src/types/cloudflare.d.ts
+// このファイルは `npm run cf-typegen` により自動生成されます。
+// 手動で編集しないでください。
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..ea5aabf
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,16 @@
+/**
+ * TailwindCSS の設定ファイル
+ * @type {import('tailwindcss').Config}
+ */
+module.exports = {
+ content: [
+ './src/app/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/components/**/*.{js,ts,jsx,tsx,mdx}'
+ ],
+ theme: {
+ extend: {
+ // カスタムテーマ設定
+ },
+ },
+ plugins: [],
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..16a006c
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/wrangler.toml b/wrangler.toml
new file mode 100644
index 0000000..09829aa
--- /dev/null
+++ b/wrangler.toml
@@ -0,0 +1,11 @@
+# wrangler.toml
+name = "prototyping-base-app-router"
+main = ".open-next/worker.js"
+compatibility_date = "2025-03-25"
+compatibility_flags = ["nodejs_compat"]
+
+[site]
+bucket = ".open-next/assets"
+
+[build]
+command = "npm run opennext:build"