This file provides comprehensive guidance for AI coding assistants (GitHub Copilot, Claude, Kiro, Cursor, and other AI models) when working with the KanaDojo codebase.
| Task | Command |
|---|---|
| Verify code | npm run check |
| Run tests | npm run test |
| Lint only | npm run lint |
| Type check only | npx tsc --noEmit |
| Validate i18n | npm run i18n:check |
Never use npm run build for verification — it takes 1-2 minutes and adds no validation value.
Windows PowerShell: Use ; to chain commands (not &&)
Linux/macOS/WSL: Use && to chain commands
# Windows PowerShell
npm run lint; npm run test
# Linux/macOS/WSL
npm run lint && npm run testKanaDojo is a Japanese learning platform built with Next.js 15, React 19, and TypeScript. It provides gamified training for Hiragana, Katakana, Kanji, and Vocabulary.
| Aspect | Technology |
|---|---|
| Framework | Next.js 15 with App Router and Turbopack |
| Language | TypeScript (strict mode) |
| Styling | Tailwind CSS + shadcn/ui |
| State | Zustand with localStorage persistence |
| i18n | next-intl (namespace-based) |
| Testing | Vitest with jsdom |
URLs: kanadojo.com · GitHub
Always use npm run check for verification (~10-30 seconds):
npm run check # TypeScript + ESLint combinednpm run lint # ESLint only
npm run lint:fix # Auto-fix ESLint issues
npx tsc --noEmit # TypeScript type checking only
npm run format # Prettier formatting
npm run format:check # Check formatting
npm run test # Run all tests (Vitest)
npm run test:watch # Watch mode
npm run i18n:check # Validate translations + generate types# Single test file
npx vitest run features/Progress/__tests__/progressUtils.test.ts
# Tests matching pattern
npx vitest run --reporter=verbose "**/*.test.ts"KanaDojo uses a feature-based architecture organized by functionality.
app/ → Pages, layouts, routing (Next.js App Router)
↓
features/ → Self-contained modules (Kana, Kanji, Vocabulary, etc.)
↓
shared/ → Reusable components, hooks, utilities
↓
core/ → Infrastructure (i18n, analytics)
kanadojo/
├── app/[locale]/ # Internationalized routes
├── features/ # Feature modules
│ ├── Kana/ # Hiragana/Katakana training
│ ├── Kanji/ # Kanji learning (JLPT levels)
│ ├── Vocabulary/ # Vocabulary training
│ ├── Progress/ # Statistics tracking
│ ├── Achievements/ # Achievement system
│ ├── Preferences/ # Themes, fonts, settings
│ └── ...
├── shared/ # Shared resources
│ ├── components/ # Reusable UI components
│ ├── hooks/ # Custom React hooks
│ ├── lib/ # Utility functions
│ └── types/ # TypeScript types
├── core/ # Infrastructure
│ ├── i18n/ # Internationalization
│ └── analytics/ # Analytics providers
└── public/ # Static assets
features/[name]/
├── components/ # React components (.tsx)
├── data/ # Static data and constants
├── lib/ # Feature utilities
├── hooks/ # Custom React hooks
├── store/ # Zustand stores
├── facade/ # Public API hooks
├── __tests__/ # Test files
└── index.ts # Barrel export (public API)
- Path aliases: Always use
@/features/,@/shared/,@/core/ - Never: Use relative imports across module boundaries
// ✅ Correct
import { KanaCards } from '@/features/Kana';
import { cn } from '@/shared/lib/utils';
// ❌ Wrong
import { KanaCards } from '../../../features/Kana/components/KanaCards';- Strict mode: Enabled — never ignore TypeScript errors
- Interfaces: Use
interfacefor public APIs,typefor unions/utilities - Naming: PascalCase for components, camelCase for variables/functions
- Hooks/Stores: Prefix with
use(e.g.,useKanaStore,useAudio)
- Components: Functional components with explicit props interfaces
- State: Zustand stores with localStorage persistence
- Memoization: Use
useMemofor expensive calculations
- Framework: Tailwind CSS + shadcn/ui
- Utility: Always use
cn()for conditional classes - Variables: Use CSS variables for theme colors
import { cn } from '@/shared/lib/utils';
<div className={cn(
'base-classes',
condition && 'conditional-classes'
)} />Zustand with localStorage persistence:
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface StoreState {
data: string[];
setData: (data: string[]) => void;
}
const useStore = create<StoreState>()(
persist(
set => ({
data: [],
setData: data => set({ data }),
}),
{ name: 'store-key' },
),
);| Store | Location | Purpose |
|---|---|---|
useKanaStore |
features/Kana/store/ |
Kana selection |
useKanjiStore |
features/Kanji/store/ |
Kanji selection |
useVocabStore |
features/Vocabulary/store/ |
Vocabulary selection |
useStatsStore |
features/Progress/store/ |
Statistics (persisted) |
usePreferencesStore |
features/Preferences/store/ |
User preferences (persisted) |
Framework: next-intl with namespace-based translations
core/i18n/locales/
├── en/ # English (reference)
├── es/ # Spanish
└── ja/ # Japanese
Usage:
import { useTranslations } from 'next-intl';
function Component() {
const t = useTranslations('common');
return <button>{t('buttons.submit')}</button>;
}Validation: Run npm run i18n:check before commits.
Use conventional commit format:
git add -A && git commit -m "<type>(<scope>): <description>"Types: feat, fix, docs, style, refactor, perf, test, chore
Example:
git add -A && git commit -m "feat(kana): add dakuon character support"- shared/: Cannot import from
features/internal directories - features/: Cannot import from other features' internal directories
- Cross-feature: Use barrel exports (
index.ts) for communication
- Circular deps: Forbidden between features
- Business logic: Keep in
features/, notapp/pages - Shared code: Only in
shared/if used by 2+ features
- Use TypeScript with proper type definitions
- Follow the feature-based architecture
- Use path aliases for imports
- Use
cn()for conditional class names - Add translations for user-facing text
- Run
npm run checkbefore committing
- Place business logic in
app/pages - Create circular dependencies between features
- Add to
shared/unless used by 2+ features - Hardcode user-facing strings
- Ignore TypeScript errors
- Use
console.log(onlywarn/errorallowed) - Use
npm run buildfor verification
- Create directory:
features/NewFeature/ - Add subdirectories:
components/,store/,data/,lib/ - Create barrel export:
features/NewFeature/index.ts - Add route:
app/[locale]/new-feature/page.tsx
- Add key to all language files in
core/i18n/locales/[lang]/ - Run
npm run i18n:validate - Use with
useTranslations('namespace')
- Add definition in
features/Preferences/data/themes.ts - Follow existing theme structure
Last Updated: January 2025