feat: [US-001] - Project scaffolding and configuration
- Initialize Next.js 16 with TypeScript and App Router - Configure TailwindCSS 4 via @tailwindcss/postcss - Install Supabase client libraries (@supabase/supabase-js, @supabase/ssr) - Create .env.example with required environment variables - Set up basic folder structure: src/app, src/components, src/lib, src/types Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
commit
a5a2732ba5
|
|
@ -0,0 +1,3 @@
|
|||
# Supabase Configuration
|
||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import nextTs from "eslint-config-next/typescript";
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
...nextTs,
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
]),
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "webvnwrite",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/ssr": "^0.8.0",
|
||||
"@supabase/supabase-js": "^2.91.0",
|
||||
"next": "16.1.4",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.1.4",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -0,0 +1,699 @@
|
|||
{
|
||||
"project": "WebVNWrite",
|
||||
"branchName": "ralph/vn-flowchart-editor",
|
||||
"description": "Visual Novel Flowchart Editor - A web-based tool for authoring visual novels with drag-and-drop nodes, branching connections, user authentication, and Ren'Py JSON export",
|
||||
"userStories": [
|
||||
{
|
||||
"id": "US-001",
|
||||
"title": "Project scaffolding and configuration",
|
||||
"description": "As a developer, I need the project set up with Next.js, TailwindCSS, and Supabase so that I can build the application.",
|
||||
"acceptanceCriteria": [
|
||||
"Initialize Next.js project with TypeScript and App Router",
|
||||
"Install and configure TailwindCSS",
|
||||
"Install Supabase client library (@supabase/supabase-js)",
|
||||
"Create .env.example with NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY",
|
||||
"Basic folder structure: app/, components/, lib/, types/",
|
||||
"Typecheck passes"
|
||||
],
|
||||
"priority": 1,
|
||||
"passes": true,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-002",
|
||||
"title": "Define TypeScript types for flowchart data",
|
||||
"description": "As a developer, I need TypeScript types for nodes, connections, and conditions.",
|
||||
"acceptanceCriteria": [
|
||||
"Create types/flowchart.ts file",
|
||||
"DialogueNode type: id, type='dialogue', position: {x,y}, data: { speaker?: string, text: string }",
|
||||
"ChoiceNode type: id, type='choice', position: {x,y}, data: { prompt: string, options: { id: string, label: string }[] }",
|
||||
"VariableNode type: id, type='variable', position: {x,y}, data: { variableName: string, operation: 'set'|'add'|'subtract', value: number }",
|
||||
"Condition type: { variableName: string, operator: '>'|'<'|'=='|'>='|'<='|'!=', value: number }",
|
||||
"FlowchartEdge type: id, source, sourceHandle?, target, targetHandle?, data?: { condition?: Condition }",
|
||||
"FlowchartData type: { nodes: (DialogueNode|ChoiceNode|VariableNode)[], edges: FlowchartEdge[] }",
|
||||
"All types exported from types/flowchart.ts",
|
||||
"Typecheck passes"
|
||||
],
|
||||
"priority": 2,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-003",
|
||||
"title": "Supabase schema for users and projects",
|
||||
"description": "As a developer, I need database tables to store users and their projects.",
|
||||
"acceptanceCriteria": [
|
||||
"Create supabase/migrations/ directory",
|
||||
"Create SQL migration file with profiles table: id (uuid, references auth.users), email (text), display_name (text), is_admin (boolean default false), created_at (timestamptz)",
|
||||
"Create projects table: id (uuid), user_id (uuid, foreign key to profiles.id), name (text), flowchart_data (jsonb), created_at (timestamptz), updated_at (timestamptz)",
|
||||
"Add RLS policy: users can SELECT/INSERT/UPDATE/DELETE their own projects (user_id = auth.uid())",
|
||||
"Add RLS policy: users can SELECT their own profile",
|
||||
"Add RLS policy: admin users (is_admin=true) can SELECT all profiles",
|
||||
"Typecheck passes"
|
||||
],
|
||||
"priority": 3,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-004",
|
||||
"title": "Supabase client configuration",
|
||||
"description": "As a developer, I need Supabase client utilities for auth and database access.",
|
||||
"acceptanceCriteria": [
|
||||
"Create lib/supabase/client.ts with browser client (createBrowserClient)",
|
||||
"Create lib/supabase/server.ts with server client (createServerClient for App Router)",
|
||||
"Create lib/supabase/middleware.ts with middleware client helper",
|
||||
"Export typed database client using generated types or manual types",
|
||||
"Typecheck passes"
|
||||
],
|
||||
"priority": 4,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-005",
|
||||
"title": "Protected routes middleware",
|
||||
"description": "As a developer, I need authentication middleware so that only logged-in users can access the app.",
|
||||
"acceptanceCriteria": [
|
||||
"Create middleware.ts at project root",
|
||||
"Middleware checks Supabase session on each request",
|
||||
"Unauthenticated users accessing /dashboard or /editor/* are redirected to /login",
|
||||
"Authenticated users accessing /login or /signup are redirected to /dashboard",
|
||||
"Public routes allowed without auth: /login, /signup, /forgot-password, /reset-password",
|
||||
"Typecheck passes"
|
||||
],
|
||||
"priority": 5,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-006",
|
||||
"title": "Login page",
|
||||
"description": "As a user, I want to log in with my email and password so that I can access my projects.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/login/page.tsx",
|
||||
"Form with email and password input fields",
|
||||
"Submit button calls Supabase signInWithPassword",
|
||||
"Show error message for invalid credentials",
|
||||
"On success, redirect to /dashboard",
|
||||
"Link to /forgot-password page",
|
||||
"Styled with TailwindCSS",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 6,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-007",
|
||||
"title": "Sign up page (invite-only)",
|
||||
"description": "As an invited user, I want to complete my account setup so that I can access the tool.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/signup/page.tsx",
|
||||
"Form with email (pre-filled if from invite link), password, and confirm password fields",
|
||||
"Validate passwords match before submission",
|
||||
"Handle Supabase invite token from URL (type=invite or type=signup)",
|
||||
"On success, create profile record in profiles table and redirect to /dashboard",
|
||||
"Show error message if signup fails",
|
||||
"Styled with TailwindCSS",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 7,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-008",
|
||||
"title": "Logout functionality",
|
||||
"description": "As a user, I want to log out so that I can secure my session.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/LogoutButton.tsx component",
|
||||
"Button calls Supabase signOut",
|
||||
"On success, redirect to /login",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 8,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-009",
|
||||
"title": "Password reset - forgot password page",
|
||||
"description": "As a user, I want to request a password reset if I forget my password.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/forgot-password/page.tsx",
|
||||
"Form with email input field",
|
||||
"Submit button calls Supabase resetPasswordForEmail",
|
||||
"Show confirmation message after sending (check your email)",
|
||||
"Link back to /login",
|
||||
"Styled with TailwindCSS",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 9,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-010",
|
||||
"title": "Password reset - set new password page",
|
||||
"description": "As a user, I want to set a new password after clicking the reset link.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/reset-password/page.tsx",
|
||||
"Form with new password and confirm password fields",
|
||||
"Handle Supabase recovery token from URL",
|
||||
"Submit calls Supabase updateUser with new password",
|
||||
"On success, redirect to /login with success message",
|
||||
"Show error if token invalid or expired",
|
||||
"Styled with TailwindCSS",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 10,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-011",
|
||||
"title": "Dashboard layout with navbar",
|
||||
"description": "As a user, I want a consistent layout with navigation so that I can move around the app.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/dashboard/layout.tsx",
|
||||
"Navbar component with app title/logo",
|
||||
"Navbar shows current user email",
|
||||
"Navbar includes LogoutButton",
|
||||
"Main content area below navbar",
|
||||
"Styled with TailwindCSS",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 11,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-012",
|
||||
"title": "Dashboard - list projects",
|
||||
"description": "As a user, I want to see all my projects so that I can choose which one to edit.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/dashboard/page.tsx",
|
||||
"Fetch projects from Supabase for current user",
|
||||
"Display projects as cards in a grid",
|
||||
"Each card shows: project name, last updated date (formatted)",
|
||||
"Click card navigates to /editor/[projectId]",
|
||||
"Empty state with message when no projects exist",
|
||||
"Loading state while fetching",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 12,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-013",
|
||||
"title": "Create new project",
|
||||
"description": "As a user, I want to create a new project so that I can start a new flowchart.",
|
||||
"acceptanceCriteria": [
|
||||
"Add 'New Project' button on dashboard",
|
||||
"Clicking opens modal with project name input",
|
||||
"Submit creates project in Supabase with empty flowchart_data: { nodes: [], edges: [] }",
|
||||
"On success, redirect to /editor/[newProjectId]",
|
||||
"Show error if creation fails",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 13,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-014",
|
||||
"title": "Delete project",
|
||||
"description": "As a user, I want to delete a project I no longer need.",
|
||||
"acceptanceCriteria": [
|
||||
"Add delete icon/button on each project card",
|
||||
"Clicking shows confirmation dialog (Are you sure?)",
|
||||
"Confirm deletes project from Supabase",
|
||||
"Project removed from dashboard list without page reload",
|
||||
"Show success toast after deletion",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 14,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-015",
|
||||
"title": "Rename project",
|
||||
"description": "As a user, I want to rename a project to keep my work organized.",
|
||||
"acceptanceCriteria": [
|
||||
"Add edit/rename icon on project card",
|
||||
"Clicking opens modal or enables inline edit for project name",
|
||||
"Submit updates project name in Supabase",
|
||||
"UI updates immediately without page reload",
|
||||
"Show error if rename fails",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 15,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-016",
|
||||
"title": "Admin - invite new user",
|
||||
"description": "As an admin, I want to invite new users so that collaborators can access the tool.",
|
||||
"acceptanceCriteria": [
|
||||
"Create app/admin/invite/page.tsx",
|
||||
"Only accessible by users with is_admin=true (redirect others to /dashboard)",
|
||||
"Form with email address input",
|
||||
"Submit calls Supabase admin inviteUserByEmail (requires service role key in server action)",
|
||||
"Show success message with invite sent confirmation",
|
||||
"Show error if invite fails",
|
||||
"Link to this page visible in navbar only for admins",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 16,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-017",
|
||||
"title": "Editor page with React Flow canvas",
|
||||
"description": "As a user, I want an editor page with a canvas where I can build my flowchart.",
|
||||
"acceptanceCriteria": [
|
||||
"Install reactflow package",
|
||||
"Create app/editor/[projectId]/page.tsx",
|
||||
"Fetch project from Supabase by ID",
|
||||
"Show error if project not found or user unauthorized",
|
||||
"Show loading state while fetching",
|
||||
"Render React Flow canvas filling the editor area",
|
||||
"Canvas has grid background (React Flow Background component)",
|
||||
"Header shows project name with back link to /dashboard",
|
||||
"Initialize React Flow with nodes and edges from flowchart_data",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 17,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-018",
|
||||
"title": "Canvas pan and zoom controls",
|
||||
"description": "As a user, I want to pan and zoom the canvas to navigate large flowcharts.",
|
||||
"acceptanceCriteria": [
|
||||
"Canvas supports click-and-drag panning (React Flow default)",
|
||||
"Mouse wheel zooms in/out (React Flow default)",
|
||||
"Add React Flow Controls component with zoom +/- buttons",
|
||||
"Add fitView button to show all nodes",
|
||||
"Controls positioned in bottom-right corner",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 18,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-019",
|
||||
"title": "Editor toolbar",
|
||||
"description": "As a user, I want a toolbar with actions for adding nodes and saving/exporting.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/editor/Toolbar.tsx",
|
||||
"Toolbar positioned at top of editor below header",
|
||||
"Buttons: Add Dialogue, Add Choice, Add Variable (no functionality yet)",
|
||||
"Buttons: Save, Export, Import (no functionality yet)",
|
||||
"Buttons styled with TailwindCSS, icons optional",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 19,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-020",
|
||||
"title": "Create custom dialogue node component",
|
||||
"description": "As a user, I want dialogue nodes to display and edit character speech.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/editor/nodes/DialogueNode.tsx",
|
||||
"Node styled with blue background/border",
|
||||
"Displays editable input for speaker name (placeholder: 'Speaker')",
|
||||
"Displays editable textarea for dialogue text (placeholder: 'Dialogue text...')",
|
||||
"Has one Handle at top (type='target', id='input')",
|
||||
"Has one Handle at bottom (type='source', id='output')",
|
||||
"Register as custom node type in React Flow",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 20,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-021",
|
||||
"title": "Add dialogue node from toolbar",
|
||||
"description": "As a user, I want to add dialogue nodes by clicking the toolbar button.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking 'Add Dialogue' in toolbar creates new DialogueNode",
|
||||
"Node appears at center of current viewport",
|
||||
"Node has unique ID (use nanoid or uuid)",
|
||||
"Node added to React Flow nodes state",
|
||||
"Node can be dragged to reposition",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 21,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-022",
|
||||
"title": "Create custom choice node component",
|
||||
"description": "As a user, I want choice nodes to display branching decisions.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/editor/nodes/ChoiceNode.tsx",
|
||||
"Node styled with green background/border",
|
||||
"Displays editable input for prompt text (placeholder: 'What do you choose?')",
|
||||
"Displays 2 default options, each with editable label input",
|
||||
"Has one Handle at top (type='target', id='input')",
|
||||
"Each option has its own Handle at bottom (type='source', id='option-0', 'option-1', etc.)",
|
||||
"Register as custom node type in React Flow",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 22,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-023",
|
||||
"title": "Add choice node from toolbar",
|
||||
"description": "As a user, I want to add choice nodes by clicking the toolbar button.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking 'Add Choice' in toolbar creates new ChoiceNode",
|
||||
"Node appears at center of current viewport",
|
||||
"Node has unique ID",
|
||||
"Node initialized with 2 options (each with unique id and empty label)",
|
||||
"Node added to React Flow nodes state",
|
||||
"Node can be dragged to reposition",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 23,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-024",
|
||||
"title": "Add/remove choice options",
|
||||
"description": "As a user, I want to add or remove choice options (2-6 options supported).",
|
||||
"acceptanceCriteria": [
|
||||
"ChoiceNode has '+' button to add new option",
|
||||
"Maximum 6 options (button disabled or hidden at max)",
|
||||
"Each option has 'x' button to remove it",
|
||||
"Minimum 2 options (remove button disabled or hidden at min)",
|
||||
"Adding option creates new output Handle dynamically",
|
||||
"Removing option removes its Handle",
|
||||
"Node data updates in React Flow state",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 24,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-025",
|
||||
"title": "Create custom variable node component",
|
||||
"description": "As a user, I want variable nodes to set or modify story variables.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/editor/nodes/VariableNode.tsx",
|
||||
"Node styled with orange background/border",
|
||||
"Displays editable input for variable name (placeholder: 'variableName')",
|
||||
"Displays dropdown/select for operation: set, add, subtract",
|
||||
"Displays editable number input for value (default: 0)",
|
||||
"Has one Handle at top (type='target', id='input')",
|
||||
"Has one Handle at bottom (type='source', id='output')",
|
||||
"Register as custom node type in React Flow",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 25,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-026",
|
||||
"title": "Add variable node from toolbar",
|
||||
"description": "As a user, I want to add variable nodes by clicking the toolbar button.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking 'Add Variable' in toolbar creates new VariableNode",
|
||||
"Node appears at center of current viewport",
|
||||
"Node has unique ID",
|
||||
"Node initialized with empty variableName, operation='set', value=0",
|
||||
"Node added to React Flow nodes state",
|
||||
"Node can be dragged to reposition",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 26,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-027",
|
||||
"title": "Connect nodes with edges",
|
||||
"description": "As a user, I want to connect nodes with arrows to define story flow.",
|
||||
"acceptanceCriteria": [
|
||||
"Dragging from source Handle to target Handle creates edge (React Flow default)",
|
||||
"Edges render as smooth bezier curves (default edge type or smoothstep)",
|
||||
"Edges show arrow marker indicating direction (markerEnd)",
|
||||
"Edges update position when nodes are moved",
|
||||
"Cannot connect source-to-source or target-to-target (React Flow handles this)",
|
||||
"New edges added to React Flow edges state",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 27,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-028",
|
||||
"title": "Select and delete nodes",
|
||||
"description": "As a user, I want to delete nodes to revise my flowchart.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking a node selects it (visual highlight via React Flow)",
|
||||
"Pressing Delete or Backspace key removes selected node(s)",
|
||||
"Deleting node also removes all connected edges",
|
||||
"Use onNodesDelete callback to handle deletion",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 28,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-029",
|
||||
"title": "Select and delete edges",
|
||||
"description": "As a user, I want to delete connections between nodes.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking an edge selects it (visual highlight via React Flow)",
|
||||
"Pressing Delete or Backspace key removes selected edge(s)",
|
||||
"Use onEdgesDelete callback to handle deletion",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 29,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-030",
|
||||
"title": "Right-click context menu",
|
||||
"description": "As a user, I want a context menu for quick actions.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/editor/ContextMenu.tsx",
|
||||
"Right-click on canvas shows menu: Add Dialogue, Add Choice, Add Variable",
|
||||
"New node created at click position",
|
||||
"Right-click on node shows menu: Delete",
|
||||
"Right-click on edge shows menu: Delete, Add Condition",
|
||||
"Clicking elsewhere or pressing Escape closes menu",
|
||||
"Menu styled with TailwindCSS",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 30,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-031",
|
||||
"title": "Condition editor modal",
|
||||
"description": "As a user, I want to add conditions to edges so branches depend on variables.",
|
||||
"acceptanceCriteria": [
|
||||
"Create components/editor/ConditionEditor.tsx modal/popover",
|
||||
"Opens on double-click edge or via context menu 'Add Condition'",
|
||||
"Form fields: variable name input, operator dropdown (>, <, ==, >=, <=, !=), value number input",
|
||||
"Pre-fill fields if edge already has condition",
|
||||
"Save button applies condition to edge data",
|
||||
"Clear/Remove button removes condition from edge",
|
||||
"Cancel button closes without saving",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 31,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-032",
|
||||
"title": "Display conditions on edges",
|
||||
"description": "As a user, I want to see conditions displayed on edges.",
|
||||
"acceptanceCriteria": [
|
||||
"Create custom edge component or use edge labels",
|
||||
"Edges with conditions render as dashed lines (strokeDasharray)",
|
||||
"Condition label displayed on edge (e.g., 'score > 5')",
|
||||
"Unconditional edges remain solid lines",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 32,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-033",
|
||||
"title": "Auto-save to LocalStorage",
|
||||
"description": "As a user, I want my work auto-saved locally so I don't lose progress if the browser crashes.",
|
||||
"acceptanceCriteria": [
|
||||
"Save flowchart state (nodes + edges) to LocalStorage on every change",
|
||||
"Debounce saves (e.g., 1 second delay after last change)",
|
||||
"LocalStorage key format: 'vnwrite-draft-{projectId}'",
|
||||
"On editor load, check LocalStorage for saved draft",
|
||||
"If local draft exists and differs from database, show prompt to restore or discard",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 33,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-034",
|
||||
"title": "Save project to database",
|
||||
"description": "As a user, I want to save my project to the database manually.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking 'Save' in toolbar saves current nodes/edges to Supabase",
|
||||
"Update project's flowchart_data and updated_at fields",
|
||||
"Show saving indicator/spinner while in progress",
|
||||
"Show success toast on completion",
|
||||
"Clear LocalStorage draft after successful save",
|
||||
"Show error toast if save fails",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 34,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-035",
|
||||
"title": "Export project as .vnflow file",
|
||||
"description": "As a user, I want to export my project as a JSON file for backup or sharing.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking 'Export' in toolbar triggers file download",
|
||||
"File named '[project-name].vnflow'",
|
||||
"File contains JSON with nodes and edges arrays",
|
||||
"JSON is pretty-printed (2-space indent) for readability",
|
||||
"Uses browser download API (create blob, trigger download)",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 35,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-036",
|
||||
"title": "Import project from .vnflow file",
|
||||
"description": "As a user, I want to import a .vnflow file to restore or share projects.",
|
||||
"acceptanceCriteria": [
|
||||
"Clicking 'Import' in toolbar opens file picker",
|
||||
"Accept .vnflow and .json file extensions",
|
||||
"If current project has unsaved changes, show confirmation dialog",
|
||||
"Validate imported file has nodes and edges arrays",
|
||||
"Show error toast if file is invalid",
|
||||
"Load valid data into React Flow state (replaces current flowchart)",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 36,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-037",
|
||||
"title": "Export to Ren'Py JSON format",
|
||||
"description": "As a user, I want to export my flowchart to Ren'Py-compatible JSON for use in my game.",
|
||||
"acceptanceCriteria": [
|
||||
"Add 'Export to Ren'Py' option (button or dropdown item)",
|
||||
"File named '[project-name]-renpy.json'",
|
||||
"Dialogue nodes export as: { type: 'dialogue', speaker: '...', text: '...' }",
|
||||
"Choice nodes export as: { type: 'menu', prompt: '...', choices: [{ label: '...', next: '...' }] }",
|
||||
"Variable nodes export as: { type: 'variable', name: '...', operation: '...', value: ... }",
|
||||
"Edges with conditions include condition object on the choice/jump",
|
||||
"Organize nodes into labeled sections based on flow (traverse from first node)",
|
||||
"Include metadata: projectName, exportedAt timestamp",
|
||||
"Output JSON is valid (test with JSON.parse)",
|
||||
"Typecheck passes"
|
||||
],
|
||||
"priority": 37,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-038",
|
||||
"title": "Unsaved changes warning",
|
||||
"description": "As a user, I want a warning before losing unsaved work.",
|
||||
"acceptanceCriteria": [
|
||||
"Track dirty state: true when flowchart modified after last save",
|
||||
"Set dirty=true on node/edge add, delete, or modify",
|
||||
"Set dirty=false after successful save",
|
||||
"Browser beforeunload event shows warning if dirty",
|
||||
"Navigating to dashboard shows confirmation modal if dirty",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 38,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"id": "US-039",
|
||||
"title": "Loading and error states",
|
||||
"description": "As a user, I want clear feedback when things are loading or when errors occur.",
|
||||
"acceptanceCriteria": [
|
||||
"Loading spinner component for async operations",
|
||||
"Editor shows loading spinner while fetching project",
|
||||
"Error message displayed if project fails to load (with back to dashboard link)",
|
||||
"Toast notification system for success/error messages",
|
||||
"Save error shows toast with retry option",
|
||||
"Typecheck passes",
|
||||
"Verify in browser using dev-browser skill"
|
||||
],
|
||||
"priority": 39,
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
## Codebase Patterns
|
||||
- Project uses Next.js 16 with App Router, TypeScript, and TailwindCSS 4
|
||||
- Source files are in `src/` directory (app, components, lib, types)
|
||||
- Supabase is configured with @supabase/supabase-js and @supabase/ssr packages
|
||||
- Environment variables follow NEXT_PUBLIC_* convention for client-side access
|
||||
|
||||
---
|
||||
|
||||
## 2026-01-21 - US-001
|
||||
- What was implemented: Project scaffolding and configuration
|
||||
- Files changed:
|
||||
- package.json - project dependencies and scripts
|
||||
- tsconfig.json - TypeScript configuration
|
||||
- next.config.ts - Next.js configuration
|
||||
- postcss.config.mjs - PostCSS with TailwindCSS
|
||||
- eslint.config.mjs - ESLint configuration
|
||||
- .env.example - environment variables template
|
||||
- .gitignore - git ignore rules
|
||||
- src/app/ - Next.js App Router pages
|
||||
- src/components/.gitkeep - components directory placeholder
|
||||
- src/lib/.gitkeep - lib directory placeholder
|
||||
- src/types/.gitkeep - types directory placeholder
|
||||
- **Learnings for future iterations:**
|
||||
- Next.js 16 uses `@tailwindcss/postcss` for TailwindCSS 4 integration
|
||||
- Use --src-dir flag for create-next-app to put source in src/ folder
|
||||
- npm package names can't have capital letters (use lowercase)
|
||||
- .gitignore needs explicit exclusion for .env files, but include .env.example
|
||||
---
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
|
|
@ -0,0 +1 @@
|
|||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
|
|
@ -0,0 +1,104 @@
|
|||
# Ralph Agent Instructions
|
||||
|
||||
You are an autonomous coding agent working on a software project.
|
||||
|
||||
## Your Task
|
||||
|
||||
1. Read the PRD at `prd.json` (in the same directory as this file)
|
||||
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
||||
3. Check you're on the correct branch from PRD `branchName`. If not, check it out or create from main.
|
||||
4. Pick the **highest priority** user story where `passes: false`
|
||||
5. Implement that single user story
|
||||
6. Run quality checks (e.g., typecheck, lint, test - use whatever your project requires)
|
||||
7. Update CLAUDE.md files if you discover reusable patterns (see below)
|
||||
8. If checks pass, commit ALL changes with message: `feat: [Story ID] - [Story Title]`
|
||||
9. Update the PRD to set `passes: true` for the completed story
|
||||
10. Append your progress to `progress.txt`
|
||||
|
||||
## Progress Report Format
|
||||
|
||||
APPEND to progress.txt (never replace, always append):
|
||||
```
|
||||
## [Date/Time] - [Story ID]
|
||||
- What was implemented
|
||||
- Files changed
|
||||
- **Learnings for future iterations:**
|
||||
- Patterns discovered (e.g., "this codebase uses X for Y")
|
||||
- Gotchas encountered (e.g., "don't forget to update Z when changing W")
|
||||
- Useful context (e.g., "the evaluation panel is in component X")
|
||||
---
|
||||
```
|
||||
|
||||
The learnings section is critical - it helps future iterations avoid repeating mistakes and understand the codebase better.
|
||||
|
||||
## Consolidate Patterns
|
||||
|
||||
If you discover a **reusable pattern** that future iterations should know, add it to the `## Codebase Patterns` section at the TOP of progress.txt (create it if it doesn't exist). This section should consolidate the most important learnings:
|
||||
|
||||
```
|
||||
## Codebase Patterns
|
||||
- Example: Use `sql<number>` template for aggregations
|
||||
- Example: Always use `IF NOT EXISTS` for migrations
|
||||
- Example: Export types from actions.ts for UI components
|
||||
```
|
||||
|
||||
Only add patterns that are **general and reusable**, not story-specific details.
|
||||
|
||||
## Update CLAUDE.md Files
|
||||
|
||||
Before committing, check if any edited files have learnings worth preserving in nearby CLAUDE.md files:
|
||||
|
||||
1. **Identify directories with edited files** - Look at which directories you modified
|
||||
2. **Check for existing CLAUDE.md** - Look for CLAUDE.md in those directories or parent directories
|
||||
3. **Add valuable learnings** - If you discovered something future developers/agents should know:
|
||||
- API patterns or conventions specific to that module
|
||||
- Gotchas or non-obvious requirements
|
||||
- Dependencies between files
|
||||
- Testing approaches for that area
|
||||
- Configuration or environment requirements
|
||||
|
||||
**Examples of good CLAUDE.md additions:**
|
||||
- "When modifying X, also update Y to keep them in sync"
|
||||
- "This module uses pattern Z for all API calls"
|
||||
- "Tests require the dev server running on PORT 3000"
|
||||
- "Field names must match the template exactly"
|
||||
|
||||
**Do NOT add:**
|
||||
- Story-specific implementation details
|
||||
- Temporary debugging notes
|
||||
- Information already in progress.txt
|
||||
|
||||
Only update CLAUDE.md if you have **genuinely reusable knowledge** that would help future work in that directory.
|
||||
|
||||
## Quality Requirements
|
||||
|
||||
- ALL commits must pass your project's quality checks (typecheck, lint, test)
|
||||
- Do NOT commit broken code
|
||||
- Keep changes focused and minimal
|
||||
- Follow existing code patterns
|
||||
|
||||
## Browser Testing (If Available)
|
||||
|
||||
For any story that changes UI, verify it works in the browser if you have browser testing tools configured (e.g., via MCP):
|
||||
|
||||
1. Navigate to the relevant page
|
||||
2. Verify the UI changes work as expected
|
||||
3. Take a screenshot if helpful for the progress log
|
||||
|
||||
If no browser tools are available, note in your progress report that manual browser verification is needed.
|
||||
|
||||
## Stop Condition
|
||||
|
||||
After completing a user story, check if ALL stories have `passes: true`.
|
||||
|
||||
If ALL stories are complete and passing, reply with:
|
||||
<promise>COMPLETE</promise>
|
||||
|
||||
If there are still stories with `passes: false`, end your response normally (another iteration will pick up the next story).
|
||||
|
||||
## Important
|
||||
|
||||
- Work on ONE story per iteration
|
||||
- Commit frequently
|
||||
- Keep CI green
|
||||
- Read the Codebase Patterns section in progress.txt before starting
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Ralph Progress Log
|
||||
Started: qua 21 jan 2026 00:46:52 -03
|
||||
---
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
#!/bin/bash
|
||||
# Ralph Wiggum - Long-running AI agent loop
|
||||
# Usage: ./ralph.sh [--tool amp|claude] [max_iterations]
|
||||
|
||||
set -e
|
||||
|
||||
# Parse arguments
|
||||
TOOL="amp" # Default to amp for backwards compatibility
|
||||
MAX_ITERATIONS=10
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--tool)
|
||||
TOOL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--tool=*)
|
||||
TOOL="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
# Assume it's max_iterations if it's a number
|
||||
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
MAX_ITERATIONS="$1"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate tool choice
|
||||
if [[ "$TOOL" != "amp" && "$TOOL" != "claude" ]]; then
|
||||
echo "Error: Invalid tool '$TOOL'. Must be 'amp' or 'claude'."
|
||||
exit 1
|
||||
fi
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PRD_FILE="$SCRIPT_DIR/prd.json"
|
||||
PROGRESS_FILE="$SCRIPT_DIR/progress.txt"
|
||||
ARCHIVE_DIR="$SCRIPT_DIR/archive"
|
||||
LAST_BRANCH_FILE="$SCRIPT_DIR/.last-branch"
|
||||
|
||||
# Archive previous run if branch changed
|
||||
if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then
|
||||
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
|
||||
LAST_BRANCH=$(cat "$LAST_BRANCH_FILE" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$CURRENT_BRANCH" ] && [ -n "$LAST_BRANCH" ] && [ "$CURRENT_BRANCH" != "$LAST_BRANCH" ]; then
|
||||
# Archive the previous run
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
# Strip "ralph/" prefix from branch name for folder
|
||||
FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||')
|
||||
ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME"
|
||||
|
||||
echo "Archiving previous run: $LAST_BRANCH"
|
||||
mkdir -p "$ARCHIVE_FOLDER"
|
||||
[ -f "$PRD_FILE" ] && cp "$PRD_FILE" "$ARCHIVE_FOLDER/"
|
||||
[ -f "$PROGRESS_FILE" ] && cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/"
|
||||
echo " Archived to: $ARCHIVE_FOLDER"
|
||||
|
||||
# Reset progress file for new run
|
||||
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
||||
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
||||
echo "---" >> "$PROGRESS_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Track current branch
|
||||
if [ -f "$PRD_FILE" ]; then
|
||||
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
|
||||
if [ -n "$CURRENT_BRANCH" ]; then
|
||||
echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Initialize progress file if it doesn't exist
|
||||
if [ ! -f "$PROGRESS_FILE" ]; then
|
||||
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
||||
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
||||
echo "---" >> "$PROGRESS_FILE"
|
||||
fi
|
||||
|
||||
echo "Starting Ralph - Tool: $TOOL - Max iterations: $MAX_ITERATIONS"
|
||||
|
||||
for i in $(seq 1 $MAX_ITERATIONS); do
|
||||
echo ""
|
||||
echo "==============================================================="
|
||||
echo " Ralph Iteration $i of $MAX_ITERATIONS ($TOOL)"
|
||||
echo "==============================================================="
|
||||
|
||||
# Run the selected tool with the ralph prompt
|
||||
if [[ "$TOOL" == "amp" ]]; then
|
||||
OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true
|
||||
else
|
||||
# Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output
|
||||
OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true
|
||||
fi
|
||||
|
||||
# Check for completion signal
|
||||
if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
|
||||
echo ""
|
||||
echo "Ralph completed all tasks!"
|
||||
echo "Completed at iteration $i of $MAX_ITERATIONS"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Iteration $i complete. Continuing..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Ralph reached max iterations ($MAX_ITERATIONS) without completing all tasks."
|
||||
echo "Check $PROGRESS_FILE for status."
|
||||
exit 1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
||||
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={100}
|
||||
height={20}
|
||||
priority
|
||||
/>
|
||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
||||
To get started, edit the page.tsx file.
|
||||
</h1>
|
||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
||||
Looking for a starting point or more instructions? Head over to{" "}
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
>
|
||||
Templates
|
||||
</a>{" "}
|
||||
or the{" "}
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
>
|
||||
Learning
|
||||
</a>{" "}
|
||||
center.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
||||
<a
|
||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Deploy Now
|
||||
</a>
|
||||
<a
|
||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,502 @@
|
|||
# PRD: Visual Novel Flowchart Editor
|
||||
|
||||
## Introduction
|
||||
|
||||
A web-based visual flowchart editor for authoring visual novels. Writers create branching narratives by placing and connecting nodes on an interactive canvas. The tool supports dialogue, choices, and variables with conditional branching. Projects are saved to a database with user authentication, and flowcharts can be exported to a flexible JSON format compatible with Ren'Py.
|
||||
|
||||
The tool is designed for private deployment with invite-only access, allowing a small team of collaborators to each manage their own projects.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide an intuitive drag-and-drop canvas for creating VN story flowcharts
|
||||
- Support three node types: dialogue, choice, and variable
|
||||
- Enable visual connections between nodes with optional conditionals
|
||||
- Auto-save to LocalStorage for crash recovery
|
||||
- Persist projects to database with user authentication
|
||||
- Support invite-only multi-user deployment (admin creates accounts)
|
||||
- Export flowcharts to flexible Ren'Py-compatible JSON
|
||||
- Deploy easily to Vercel or Netlify
|
||||
|
||||
## User Stories
|
||||
|
||||
### US-001: Project scaffolding and configuration
|
||||
**Description:** As a developer, I need the project set up with Next.js, TailwindCSS, and Supabase so that I can build the application.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Initialize Next.js project with TypeScript and App Router
|
||||
- [ ] Install and configure TailwindCSS
|
||||
- [ ] Install Supabase client library (@supabase/supabase-js)
|
||||
- [ ] Create `.env.example` with required Supabase environment variables
|
||||
- [ ] Basic folder structure: `app/`, `components/`, `lib/`, `types/`
|
||||
- [ ] Typecheck passes
|
||||
|
||||
### US-002: Supabase schema for users and projects
|
||||
**Description:** As a developer, I need database tables to store users and their projects.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Create `profiles` table: id (references auth.users), email, display_name, is_admin, created_at
|
||||
- [ ] Create `projects` table: id, user_id (foreign key to profiles), name, flowchart_data (JSONB), created_at, updated_at
|
||||
- [ ] Set up Row Level Security (RLS): users can only access their own projects
|
||||
- [ ] Admin users can access all profiles (for invite system)
|
||||
- [ ] SQL migration file saved in `supabase/migrations/`
|
||||
- [ ] Typecheck passes
|
||||
|
||||
### US-003: Sign up page (invite-only)
|
||||
**Description:** As an invited user, I want to create my account using an invite link so that I can access the tool.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `/signup` page with email and password fields
|
||||
- [ ] Sign up only works with valid Supabase invite token (magic link or pre-created auth user)
|
||||
- [ ] Show error message if signup fails or token invalid
|
||||
- [ ] On success, create profile record and redirect to dashboard
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-004: Login page
|
||||
**Description:** As a user, I want to log in with my email and password so that I can access my projects.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `/login` page with email and password fields
|
||||
- [ ] Show error message for invalid credentials
|
||||
- [ ] On success, redirect to `/dashboard`
|
||||
- [ ] Link to "Forgot password" page
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-005: Logout functionality
|
||||
**Description:** As a user, I want to log out so that I can secure my session.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Logout button visible in header/navbar when authenticated
|
||||
- [ ] Clicking logout clears session and redirects to `/login`
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-006: Password reset flow
|
||||
**Description:** As a user, I want to reset my password if I forget it.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `/forgot-password` page with email field
|
||||
- [ ] Sends password reset email via Supabase
|
||||
- [ ] Shows confirmation message after sending
|
||||
- [ ] `/reset-password` page to set new password (handles Supabase token)
|
||||
- [ ] On success, redirect to login with success message
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-007: Protected routes middleware
|
||||
**Description:** As a developer, I need authentication middleware so that only logged-in users can access the app.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Middleware checks Supabase session on protected routes
|
||||
- [ ] Unauthenticated users redirected to `/login`
|
||||
- [ ] Protected routes: `/dashboard`, `/editor/*`
|
||||
- [ ] Public routes: `/login`, `/signup`, `/forgot-password`, `/reset-password`
|
||||
- [ ] Typecheck passes
|
||||
|
||||
### US-008: User dashboard - list projects
|
||||
**Description:** As a user, I want to see all my projects so that I can choose which one to edit.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `/dashboard` page shows list of user's projects
|
||||
- [ ] Each project card shows: name, last updated date
|
||||
- [ ] Click project card to open in editor (`/editor/[projectId]`)
|
||||
- [ ] Empty state with message when no projects exist
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-009: Create new project
|
||||
**Description:** As a user, I want to create a new project so that I can start a new flowchart.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] "New Project" button on dashboard
|
||||
- [ ] Opens modal to enter project name
|
||||
- [ ] Creates project in database with empty flowchart_data
|
||||
- [ ] Redirects to editor with new project loaded
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-010: Delete project
|
||||
**Description:** As a user, I want to delete a project I no longer need.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Delete button/icon on each project card
|
||||
- [ ] Shows confirmation dialog before deleting
|
||||
- [ ] Deletes project from database
|
||||
- [ ] Removes project from dashboard list
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-011: Rename project
|
||||
**Description:** As a user, I want to rename a project to keep my work organized.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Edit/rename button on project card or in editor header
|
||||
- [ ] Opens modal or inline edit for project name
|
||||
- [ ] Saves new name to database
|
||||
- [ ] Updates display immediately
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-012: Admin - invite new user
|
||||
**Description:** As an admin, I want to invite new users so that collaborators can access the tool.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Admin-only page or section: `/admin/invite`
|
||||
- [ ] Form to enter email address
|
||||
- [ ] Creates Supabase auth user and sends invite email
|
||||
- [ ] Shows success/error message
|
||||
- [ ] Only accessible by users with is_admin=true
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-013: Define TypeScript types for flowchart data
|
||||
**Description:** As a developer, I need TypeScript types for nodes, connections, and conditions.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `DialogueNode` type: id, type, position, data: { speaker, text }
|
||||
- [ ] `ChoiceNode` type: id, type, position, data: { prompt, options: { id, label }[] }
|
||||
- [ ] `VariableNode` type: id, type, position, data: { variableName, operation, value }
|
||||
- [ ] `FlowchartEdge` type: id, source, sourceHandle, target, targetHandle, data: { condition? }
|
||||
- [ ] `Condition` type: variableName, operator, value
|
||||
- [ ] `FlowchartData` type: { nodes: Node[], edges: Edge[] }
|
||||
- [ ] Types exported from `types/flowchart.ts`
|
||||
- [ ] Typecheck passes
|
||||
|
||||
### US-014: Editor page with canvas
|
||||
**Description:** As a user, I want an editor page with a canvas where I can build my flowchart.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `/editor/[projectId]` page loads project from database
|
||||
- [ ] React Flow canvas fills the editor area
|
||||
- [ ] Canvas has grid background
|
||||
- [ ] Header shows project name
|
||||
- [ ] Shows loading state while fetching project
|
||||
- [ ] Shows error if project not found or unauthorized
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-015: Canvas pan and zoom
|
||||
**Description:** As a user, I want to pan and zoom the canvas to navigate large flowcharts.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Click and drag on canvas background to pan
|
||||
- [ ] Mouse wheel to zoom in/out
|
||||
- [ ] Zoom controls (+ / - buttons) in corner
|
||||
- [ ] Fit-to-view button to show all nodes
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-016: Editor toolbar
|
||||
**Description:** As a user, I want a toolbar with actions for adding nodes and saving/exporting.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Toolbar at top or side of editor
|
||||
- [ ] Buttons: Add Dialogue, Add Choice, Add Variable
|
||||
- [ ] Buttons: Save, Export, Import
|
||||
- [ ] Back to Dashboard button/link
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-017: Add dialogue node
|
||||
**Description:** As a user, I want to add dialogue nodes to represent character speech.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Clicking "Add Dialogue" creates node at center of viewport
|
||||
- [ ] Node styled with distinct color (e.g., blue)
|
||||
- [ ] Node displays editable speaker name field
|
||||
- [ ] Node displays editable text field (multi-line)
|
||||
- [ ] Node has one input handle (top) and one output handle (bottom)
|
||||
- [ ] Node can be dragged to reposition
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-018: Add choice node
|
||||
**Description:** As a user, I want to add choice nodes to create branching decisions.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Clicking "Add Choice" creates node at center of viewport
|
||||
- [ ] Node styled with distinct color (e.g., green)
|
||||
- [ ] Node displays editable prompt text field
|
||||
- [ ] Node starts with 2 options, each with editable label
|
||||
- [ ] Each option has its own output handle
|
||||
- [ ] Node has one input handle (top)
|
||||
- [ ] Node can be dragged to reposition
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-019: Add/remove choice options
|
||||
**Description:** As a user, I want to add or remove choice options (2-6 options supported).
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] "+" button on choice node to add option (max 6)
|
||||
- [ ] "x" button on each option to remove (min 2)
|
||||
- [ ] Adding option creates new output handle
|
||||
- [ ] Removing option deletes connected edge if any
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-020: Add variable node
|
||||
**Description:** As a user, I want to add variable nodes to set or modify story variables.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Clicking "Add Variable" creates node at center of viewport
|
||||
- [ ] Node styled with distinct color (e.g., orange)
|
||||
- [ ] Node displays editable variable name field
|
||||
- [ ] Node displays operation dropdown: set, add, subtract
|
||||
- [ ] Node displays editable numeric value field
|
||||
- [ ] Node has one input handle and one output handle
|
||||
- [ ] Node can be dragged to reposition
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-021: Connect nodes with edges
|
||||
**Description:** As a user, I want to connect nodes with arrows to define story flow.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Drag from output handle to input handle to create edge
|
||||
- [ ] Edges render as smooth bezier curves
|
||||
- [ ] Edges show arrow marker indicating direction
|
||||
- [ ] Edges update position when nodes are moved
|
||||
- [ ] Cannot connect output-to-output or input-to-input
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-022: Select and delete nodes
|
||||
**Description:** As a user, I want to delete nodes to revise my flowchart.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Click node to select it (visual highlight)
|
||||
- [ ] Press Delete key to remove selected node
|
||||
- [ ] Deleting node removes all its connected edges
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-023: Select and delete edges
|
||||
**Description:** As a user, I want to delete connections between nodes.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Click edge to select it (visual highlight)
|
||||
- [ ] Press Delete key to remove selected edge
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-024: Right-click context menu
|
||||
**Description:** As a user, I want a context menu for quick actions.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Right-click on canvas: menu with Add Dialogue, Add Choice, Add Variable
|
||||
- [ ] Node appears at click position
|
||||
- [ ] Right-click on node: menu with Delete option
|
||||
- [ ] Right-click on edge: menu with Delete, Add Condition options
|
||||
- [ ] Clicking elsewhere closes menu
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-025: Add condition to edge
|
||||
**Description:** As a user, I want to add conditions to edges so branches depend on variables.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Double-click edge or use context menu to open condition editor
|
||||
- [ ] Editor modal/popover with: variable name input, operator dropdown (>, <, ==, >=, <=, !=), value input
|
||||
- [ ] Save button applies condition to edge
|
||||
- [ ] Clear/Remove button removes condition
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-026: Display conditions on edges
|
||||
**Description:** As a user, I want to see conditions displayed on edges.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Edges with conditions render as dashed lines
|
||||
- [ ] Condition label displayed on edge (e.g., "score > 5")
|
||||
- [ ] Unconditional edges remain solid lines
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-027: Auto-save to LocalStorage
|
||||
**Description:** As a user, I want my work auto-saved locally so I don't lose progress if the browser crashes.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Flowchart state saved to LocalStorage on every change (debounced, e.g., 1 second)
|
||||
- [ ] LocalStorage key includes project ID to support multiple projects
|
||||
- [ ] On editor load, check LocalStorage for newer data than database
|
||||
- [ ] If local data is newer, prompt user to restore or discard
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-028: Save project to database
|
||||
**Description:** As a user, I want to save my project to the database manually.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] "Save" button in toolbar
|
||||
- [ ] Saves current flowchart_data to database
|
||||
- [ ] Shows saving indicator while in progress
|
||||
- [ ] Shows success toast/message on completion
|
||||
- [ ] Clears LocalStorage draft after successful save
|
||||
- [ ] Updates project's updated_at timestamp
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-029: Export project as .vnflow file
|
||||
**Description:** As a user, I want to export my project as a JSON file for backup or sharing.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] "Export" button in toolbar (or dropdown)
|
||||
- [ ] Downloads file as `[project-name].vnflow`
|
||||
- [ ] File contains full flowchart data (nodes + edges)
|
||||
- [ ] JSON is human-readable (pretty-printed)
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-030: Import project from .vnflow file
|
||||
**Description:** As a user, I want to import a .vnflow file to restore or share projects.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] "Import" button opens file picker
|
||||
- [ ] Accepts .vnflow or .json files
|
||||
- [ ] Prompts for confirmation if current project has unsaved changes
|
||||
- [ ] Loads flowchart data into current project
|
||||
- [ ] Validates file structure before importing
|
||||
- [ ] Shows error message if file is invalid
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-031: Export to Ren'Py JSON format
|
||||
**Description:** As a user, I want to export my flowchart to Ren'Py-compatible JSON for use in my game.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] "Export to Ren'Py" button/option
|
||||
- [ ] Downloads file as `[project-name]-renpy.json`
|
||||
- [ ] Dialogue nodes export as: `{ "type": "dialogue", "speaker": "...", "text": "..." }`
|
||||
- [ ] Choice nodes export as: `{ "type": "menu", "prompt": "...", "choices": [{ "label": "...", "next": "..." }] }`
|
||||
- [ ] Variable nodes export as: `{ "type": "variable", "name": "...", "operation": "...", "value": ... }`
|
||||
- [ ] Edges with conditions include: `"condition": { "variable": "...", "operator": "...", "value": ... }`
|
||||
- [ ] Nodes organized into labeled sections based on flow structure
|
||||
- [ ] Output JSON is valid and parseable
|
||||
- [ ] Typecheck passes
|
||||
|
||||
### US-032: Unsaved changes warning
|
||||
**Description:** As a user, I want a warning before losing unsaved work.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Track dirty state when flowchart is modified after last save
|
||||
- [ ] Browser beforeunload shows warning if dirty
|
||||
- [ ] Navigating away (e.g., back to dashboard) shows confirmation modal if dirty
|
||||
- [ ] Dirty state clears after successful save
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
### US-033: Loading and error states
|
||||
**Description:** As a user, I want clear feedback when things are loading or when errors occur.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Loading spinner while fetching project data
|
||||
- [ ] Error message if project fails to load
|
||||
- [ ] Error message if save fails (with retry option)
|
||||
- [ ] Toast notifications for success/error feedback
|
||||
- [ ] Typecheck passes
|
||||
- [ ] Verify in browser using dev-browser skill
|
||||
|
||||
## Functional Requirements
|
||||
|
||||
- FR-1: The system must authenticate users via Supabase Auth with email/password
|
||||
- FR-2: The system must restrict registration to invite-only (admin creates users)
|
||||
- FR-3: The system must store user profiles with admin flag for access control
|
||||
- FR-4: The system must store projects with JSONB flowchart data per user
|
||||
- FR-5: The system must enforce Row Level Security so users only access their own projects
|
||||
- FR-6: The system must render an interactive canvas using React Flow
|
||||
- FR-7: The system must support three node types: dialogue, choice, variable
|
||||
- FR-8: Dialogue nodes must store: speaker name (optional), text content
|
||||
- FR-9: Choice nodes must store: prompt text, 2-6 options with labels
|
||||
- FR-10: Variable nodes must store: variable name, operation (set/add/subtract), numeric value
|
||||
- FR-11: The system must allow creating edges by dragging between handles
|
||||
- FR-12: Edges must support optional conditions: variable name, operator, numeric value
|
||||
- FR-13: The system must auto-save to LocalStorage on changes (debounced)
|
||||
- FR-14: The system must support manual save to database
|
||||
- FR-15: The system must support export/import as .vnflow JSON files
|
||||
- FR-16: The system must export to Ren'Py-compatible JSON format
|
||||
- FR-17: The system must warn users before losing unsaved changes
|
||||
|
||||
## Non-Goals (Out of Scope)
|
||||
|
||||
- No undo/redo functionality
|
||||
- No keyboard shortcuts beyond Delete key for deletion
|
||||
- No real-time collaborative editing
|
||||
- No cloud auto-sync (manual save only)
|
||||
- No asset management (images, sprites, audio)
|
||||
- No preview/playback mode to test the VN
|
||||
- No auto-layout or alignment algorithms
|
||||
- No copy/paste of nodes
|
||||
- No public registration (invite-only)
|
||||
- No node grouping or sub-flowcharts
|
||||
|
||||
## Technical Considerations
|
||||
|
||||
- **Framework:** Next.js 14+ with App Router and TypeScript
|
||||
- **Styling:** TailwindCSS for utility-first styling
|
||||
- **Canvas:** React Flow library for node-based editor
|
||||
- **Auth & Database:** Supabase (PostgreSQL + Auth)
|
||||
- **State Management:** React state + React Flow's built-in state; consider Zustand if complexity grows
|
||||
- **LocalStorage:** Use project ID as key prefix to support multiple drafts
|
||||
- **Deployment:** Vercel or Netlify (both support Next.js natively)
|
||||
|
||||
### Supabase Setup Notes
|
||||
- Enable Email Auth in Supabase dashboard
|
||||
- Disable "Enable email confirmations" for simpler invite flow, OR use magic links
|
||||
- Use Supabase's `inviteUserByEmail` function for admin invites
|
||||
- RLS policies must be carefully tested
|
||||
|
||||
### Ren'Py Export Format (Flexible)
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"exportedAt": "2024-01-15T10:30:00Z",
|
||||
"projectName": "My VN"
|
||||
},
|
||||
"variables": {
|
||||
"affection": 0,
|
||||
"seen_intro": false
|
||||
},
|
||||
"labels": {
|
||||
"start": [
|
||||
{ "type": "dialogue", "speaker": "Alice", "text": "Hello!" },
|
||||
{ "type": "menu", "prompt": "What do you say?", "choices": [
|
||||
{ "label": "Hi!", "next": "friendly_path" },
|
||||
{ "label": "Go away", "next": "rude_path", "condition": { "variable": "confidence", "operator": ">", "value": 5 } }
|
||||
]}
|
||||
],
|
||||
"friendly_path": [
|
||||
{ "type": "variable", "name": "affection", "operation": "add", "value": 1 },
|
||||
{ "type": "dialogue", "speaker": "Alice", "text": "Nice to meet you!" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Design Considerations
|
||||
|
||||
- Clean, minimal UI with focus on the canvas
|
||||
- Node colors should clearly differentiate types:
|
||||
- Dialogue: Blue
|
||||
- Choice: Green
|
||||
- Variable: Orange
|
||||
- Consistent spacing and typography via TailwindCSS
|
||||
- Responsive layout, but optimized for desktop (canvas editing is difficult on mobile)
|
||||
- Dark mode support is nice-to-have but not required for MVP
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- User can create a branching story (10+ nodes) and save it
|
||||
- User can close browser and return to find project intact
|
||||
- Exported Ren'Py JSON is valid and usable
|
||||
- Invited collaborators can sign up and manage their own projects
|
||||
- Application deploys successfully to Vercel/Netlify
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should we support string variables in addition to numeric?
|
||||
- Should conditions support AND/OR logic (multiple conditions per edge)?
|
||||
- What should happen if a user tries to connect to an already-connected input?
|
||||
- Should we add a "Start" node type to mark the entry point?
|
||||
- Should project names be unique per user?
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts",
|
||||
"**/*.mts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Loading…
Reference in New Issue