{displayTitle}
- - {roadmap.mode === "intern" ? "Intern" : "General"} - -{getRoadmapStateLabel(stats.progressState)}
-State
-{getRoadmapStateLabel(stats.progressState)}
-Updated
-{getRelativeTimeLabel(roadmap.updatedAt || roadmap.createdAt)}
-diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..45159ed --- /dev/null +++ b/.env.example @@ -0,0 +1,32 @@ +# Public origin used for canonical URLs, Open Graph/Twitter tags, robots.txt +# and sitemap.xml. No trailing slash. +VITE_APP_URL=http://localhost:3000 + +# Leave these blank for local mode. Deployment URLs are public, never API keys. +VITE_CONVEX_URL= +VITE_CONVEX_SITE_URL= + +# Local production host listens on loopback by default. +# PORT=3000 +# HOST=127.0.0.1 + +# --- Scout ----------------------------------------------------------------- +# Scout keeps one SQLite file. Defaults to .data/syllo.db in the repo. +# SCOUT_DB_PATH=C:\path\to\syllo.db + +# Free-only AI. Use an unbilled Google AI Studio project. +# Browser Settings can supply a key instead; it is not synced. +SCOUT_PROVIDER=gemini +SCOUT_MODEL=gemini-2.5-flash + +GEMINI_API_KEY= + +# Optional YouTube Data API key for broader video search and metadata. +# Without it, creator RSS and Gemini grounded search supply candidates. +YOUTUBE_API_KEY= +# Articles use keyless Jina Reader and direct page fetches. +# Paid Exa/Supadata fallbacks and authenticated Jina requests are disabled. + +# Delay between YouTube transcript attempts, in ms. Defaults to 150000 +# (2.5 minutes); values below 30000 are raised to the floor. +# SCOUT_TRANSCRIPT_INTERVAL_MS=150000 diff --git a/.gitignore b/.gitignore index f7b27d4..423b457 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ node_modules -.next +dist +.tanstack .env .env.local -.env.example +.env.*.local coverage -dist +.data *.log *.tsbuildinfo .agents\skills -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/CONTEXT.md b/CONTEXT.md index df1dead..f4f5ba9 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,21 +1,41 @@ # Roadmap Studio -Roadmap Studio turns source material into workspaces where a learner can plan, study, and track progress. +Roadmap Studio turns source material into roadmaps where a learner can plan, study, and track progress. ## Language -**Workspace**: -The learner's editable collection of sections, tasks, resources, progress, and settings for one goal. -_Avoid_: Course, project +**Roadmap**: +The learner's editable collection of modules, tasks, resources, notes, review cards, and settings for one goal. +_Avoid_: Workspace, course, project + +**Module**: +One skill inside a Roadmap, holding the tasks, notes, and resources that teach it. +_Avoid_: Section, chapter, lesson + +**Focused Roadmap**: +A Roadmap the learner has chosen to work on now. At most three Roadmaps are focused at once; only focused Roadmaps feed Today. +_Avoid_: Active roadmap, pinned roadmap + +**Next Step**: +The first unfinished task in a Roadmap, in module order. +_Avoid_: Current task, up next + +**Today**: +The queue of actions waiting for the learner: resuming a video, review cards due, the Next Step of each Focused Roadmap, Picks to approve, and the next book chapter. +_Avoid_: Dashboard, home feed + +**Pick**: +A resource Scout proposes for a goal, waiting for the learner to keep or skip it. +_Avoid_: Suggestion, result **Learning Session**: -A period in which a learner works in one Workspace and produces progress, review, timing, or achievement changes. +A period in which a learner works in one Roadmap and produces progress, review, timing, or achievement changes. _Avoid_: Visit, browser session **Learning Record**: -The durable history of learning activity across Workspaces, including streaks, achievements, and daily task counts. +The durable history of learning activity across Roadmaps, including streaks, achievements, and daily task counts. _Avoid_: Analytics data, usage data **Roadmap Intake**: -The conversion of source material and learner choices into a validated Workspace draft. +The conversion of source material and learner choices into a validated Roadmap draft, either from a goal through Scout or from the learner's own notes. _Avoid_: Parsing, generation diff --git a/README.md b/README.md index 4418061..c0e7e3f 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,58 @@ # Syllo -ZNS RoadMap Studio turns raw guides, playbooks, curricula, and notes into interactive learning workspaces. Paste content or upload a markdown file, let the AI structure it, and work through modules, tasks, resources, notes, and progress tracking in one place. +Syllo is a personal learning and reading app. Resume a lesson, read an EPUB, keep notes tied to a source, and review concepts you choose to remember. -## Features +The Spacious design provides Home, Library, Learn, Reviews, Scout, and Settings. Home shows your actual topics and reading position. Library offers book spines or a searchable list. The EPUB reader uses the book's real contents and saves passages, highlights, and your place. Lessons embed videos with saved timestamps and an optional Pomodoro. -- AI-powered parsing for roadmaps, playbooks, tutorials, strategies, and other structured content -- Interactive workspace with modules, milestones, notes, resources, videos, and progress tracking -- Local-first persistence — all data stays in your browser -- Markdown, JSON, and PDF export -- User-configurable AI provider, model, and API key +Scout finds free resources, reads the available source text, and asks you to keep or skip its picks. A generated roadmap stays a draft until you edit and approve it. Suggested review cards only enter the FSRS schedule when kept. Manual resource entry works without AI. -## Stack +## Run locally -- Next.js App Router -- React 19 -- TypeScript -- Tailwind CSS v4 +Use Node 22.12 or newer and npm: -## Getting Started +```powershell +npm ci +npm run dev +``` -1. Install dependencies: +Open `http://localhost:3000`. Convex is optional. With `VITE_CONVEX_URL` blank, progress uses browser storage and EPUB files use IndexedDB. No account is needed. -```bash -npm install -``` +Read [setup instructions](docs/syllo-setup.md) for optional private Convex sync, free Gemini configuration, the local transcript worker, Android use, and backups. No Convex project is configured in this checkout. -2. Create `.env.local` with the variables you need: +## Stack and data -```env -# Access gate (password lock) - required -GATE_PASSWORD=change-me-to-a-strong-password +TanStack Start and Router, React 19, TypeScript, Tailwind v4, epub.js, ts-fsrs, and an optional Convex backend. Scout runs in the Node server with a local SQLite database. It needs a Gemini key from an unbilled Google AI Studio project for generation; paid provider fallbacks are disabled. -# Optional: your own AI provider keys (used server-side for shared studio AI) -AI_PROVIDER=gemini -AI_API_KEY= -GEMINI_API_KEY= -OPENAI_API_KEY= -GROQ_API_KEY= -OPENROUTER_API_KEY= -NEXT_PUBLIC_APP_URL=http://localhost:3000 -``` +Existing workspace routes, editing tools, and the `zns:v2:workspace-state` storage key remain supported. The new bridge preserves an original local backup before syncing roadmaps and review cards. Settings exports metadata, including the legacy workspace backup. Original EPUBs and API keys are excluded. -3. Start the app: +Convex sync is prepared for one owner, with password login restricted by `SYLLO_OWNER_EMAIL` and authenticated file downloads. Without a configured deployment, all local features remain available. Live cloud integration still needs a project and account setup. -```bash -npm run dev -``` +## Development -## Scripts +```powershell +npm run typecheck +npm run typecheck:convex +npm test +npm run lint +npm run build +npm start +``` -- `npm run dev` - start the local dev server -- `npm run build` - run a production build -- `npm run start` - start the production server -- `npm run lint` - run ESLint -- `npm run typecheck` - run TypeScript without emitting files +The production server serves `dist/client` and the TanStack SSR handler on loopback port 3000. `npm start` does not read an env file automatically; pass environment variables or use `node --env-file=.env.local server.mjs`. Deployment URLs are build-time settings. -## Project Notes +`mcp/server.mjs` supplies a local stdio MCP server for coding agents to add and search resource candidates. Connection snippets are available in Settings. -- All data is stored locally in the browser (workspaces, notes, progress, credits). -- Custom AI keys are stored in browser storage and sent only to the selected provider when used. -- Optional server-side AI keys can be set in `.env.local` for shared studio AI. +## Project layout -## Open Source +```text +src/routes/ Routes and server API handlers +src/views/studio/ Learning, reading, review, Scout, and settings pages +src/components/learning/ EPUB covers, video player, cards, and Pomodoro +src/components/sync/ Optional Convex provider and local workspace bridge +src/lib/personal/ Validated records, browser files, and sync references +src/lib/scout/ Discovery, extraction, planning, SQLite, and jobs +convex/ Private account, document sync, and file functions +mcp/ Local coding-agent resource tools +``` -This repository is still in active development. Expect UI and schema changes while the workspace model evolves. +This app is personal-first. Forks can use their own deployment, books, preferences, and keys. Public publishing and multi-user collaboration are outside v1. Earlier gallery/share routes remain in the codebase; personal sync does not publish learning records through them. diff --git a/app/(marketing)/gallery/loading.tsx b/app/(marketing)/gallery/loading.tsx deleted file mode 100644 index aee7d85..0000000 --- a/app/(marketing)/gallery/loading.tsx +++ /dev/null @@ -1,63 +0,0 @@ -"use client"; - -import { motion } from "framer-motion"; - -export default function GalleryLoading() { - const skeletonCards = Array.from({ length: 6 }, (_, index) => index); - - return ( -
{label}
-{value}
- {sub &&{sub}
} -Analytics
-- Track your consistency, celebrate milestones, and see the full picture of your learning journey. -
-No workspaces yet.
- - Create your first roadmap → - -- {stats.completedTasks} / {stats.totalTasks} tasks · {stats.moduleCount} module{stats.moduleCount !== 1 ? "s" : ""} -
-Complete modules and roadmaps to unlock milestones.
-{ms.label}
-{globalStats.overallPct}%
-Content Import
-- Drop in your guides, PDFs (as text for now), or markdown. We'll extract the steps and modules. -
-Parsing Mode
-Please wait while the AI breaks down your content.
-Review the generated structure before creating your workspace.
-{upgradeMessage}
-Dashboard
-- Jump back into your active workspaces and keep your learning streak alive. -
-You have no active workspaces right now.
- -Library
-- Open, sort, and continue work from one place. -
-Local-only storage
-- Everything stays in this browser. No account or cloud sync required. -
-- {roadmaps.length === 0 - ? "Create the first workspace and it will appear here as a clean, sortable library." - : "Adjust the filters or sorting to bring another set of workspaces back into view."} -
-- Embed unavailable -
-- This workspace is private or no longer exists. -
-- This project is currently under{" "} - passive development and will be - revealed soon. While the work is still in progress, access is limited to a small - circle of people. -
- -- Under construction · Details will be announced shortly -
-{getRoadmapStateLabel(stats.progressState)}
-State
-{getRoadmapStateLabel(stats.progressState)}
-Updated
-{getRelativeTimeLabel(roadmap.updatedAt || roadmap.createdAt)}
-
- Link
-
-
- `);
- });
-
- await new PromiseAt-risk modules
-{roadmap.title}
-+
{roadmap.title}
+Completion is {Math.round(getRoadmapCompletionRate(roadmap) * 100)}%. Review tasks, unblock the next module, and log a coaching follow-up.
Coaching log
-Coaching log
-{message}
: null} + {message ?{message}
: null}Summary
-{review.summary}
+Summary
+{review.summary}
Strengths
-Strengths
+Risks
-Risks
+Next actions
-Next actions
+Completion table
-Completion table
-| Workspace | Mode | Tasks | @@ -44,30 +42,30 @@ export default function ModuleCompletionTable({ roadmaps }: { roadmaps: Roadmap[ const totals = getRoadmapTaskTotals(roadmap); const rate = Math.round(getRoadmapCompletionRate(roadmap) * 100); return ( -||||
|---|---|---|---|---|---|---|
|
-
{roadmap.title} -{roadmap.summary ?? "No summary"} +{roadmap.title} +{roadmap.summary ?? "No summary"} |
- {roadmap.mode} | -+ | {roadmap.mode} | +{totals.completedTasks}/{totals.totalTasks} |
|
- + Open | diff --git a/components/dashboard/SkillHeatmap.tsx b/src/components/dashboard/SkillHeatmap.tsx similarity index 75% rename from components/dashboard/SkillHeatmap.tsx rename to src/components/dashboard/SkillHeatmap.tsx index 319a37b..5edb774 100644 --- a/components/dashboard/SkillHeatmap.tsx +++ b/src/components/dashboard/SkillHeatmap.tsx @@ -3,10 +3,10 @@ import { getRoadmapCompletionRate } from "@/lib/analytics"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; function getTone(rate: number) { - if (rate >= 0.85) return "bg-[rgba(22,20,17,0.9)]"; - if (rate >= 0.6) return "bg-[rgba(22,20,17,0.72)]"; - if (rate >= 0.35) return "bg-[rgba(22,20,17,0.48)]"; - return "bg-[rgba(22,20,17,0.22)]"; + if (rate >= 0.85) return "bg-accent"; + if (rate >= 0.6) return "bg-accent/70"; + if (rate >= 0.35) return "bg-accent/45"; + return "bg-accent/20"; } export default function SkillHeatmap({ roadmaps }: { roadmaps: Roadmap[] }) { @@ -14,11 +14,10 @@ export default function SkillHeatmap({ roadmaps }: { roadmaps: Roadmap[] }) { return (