Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ coverage
*.tsbuildinfo
.agents\skills
CLAUDE.md

# Local design scratch and agent tooling
.design
.taste-skill
42 changes: 23 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Scout finds free resources, reads the available source text, and asks you to kee

## Run locally

Use Node 22.12 or newer and npm:
Use Node 22.12 or newer and pnpm. Run commands from the repo root unless noted:

```powershell
npm ci
npm run dev
pnpm install --frozen-lockfile
pnpm dev
```

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.
Expand All @@ -30,29 +30,33 @@ Convex sync is prepared for one owner, with password login restricted by `SYLLO_
## Development

```powershell
npm run typecheck
npm run typecheck:convex
npm test
npm run lint
npm run build
npm start
pnpm typecheck
pnpm test
pnpm lint
pnpm build
pnpm start
```

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.
`pnpm typecheck` checks both the app and `apps/web/convex/`.

`mcp/server.mjs` supplies a local stdio MCP server for coding agents to add and search resource candidates. Connection snippets are available in Settings.
The production server serves `dist/client` and the TanStack SSR handler on loopback port 3000. `pnpm start` does not read an env file automatically; pass environment variables or run `node --env-file=.env.local server.mjs` in `apps/web`. Deployment URLs are build-time settings.

`apps/web/mcp/server.mjs` supplies a local stdio MCP server for coding agents to add and search resource candidates. Connection snippets are available in Settings.

## Project layout

The repo is a pnpm workspace. Each app lives under `apps/`, and code shared between apps goes under `packages/`.

```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
apps/web/ The Syllo app (TanStack Start), its env files and local .data/
apps/web/src/routes/ Routes and server API handlers
apps/web/src/views/studio/ Learning, reading, review, Scout, and settings pages
apps/web/src/components/learning/ EPUB covers, video player, cards, and Pomodoro
apps/web/src/components/sync/ Optional Convex provider and local workspace bridge
apps/web/src/lib/personal/ Validated records, browser files, and sync references
apps/web/src/lib/scout/ Discovery, extraction, planning, SQLite, and jobs
apps/web/convex/ Private account, document sync, and file functions
apps/web/mcp/ Local coding-agent resource tools
```

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.
2 changes: 1 addition & 1 deletion .env.example → apps/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ VITE_CONVEX_SITE_URL=
# HOST=127.0.0.1

# --- Scout -----------------------------------------------------------------
# Scout keeps one SQLite file. Defaults to .data/syllo.db in the repo.
# Scout keeps one SQLite file. Defaults to apps/web/.data/syllo.db.
# SCOUT_DB_PATH=C:\path\to\syllo.db

# Free-only AI. Use an unbilled Google AI Studio project.
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
76 changes: 76 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"name": "@syllo/web",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"start": "node server.mjs",
"lint": "eslint .",
"typecheck": "tsc --noEmit && tsc -p convex/tsconfig.json",
"typecheck:convex": "tsc -p convex/tsconfig.json",
"convex:dev": "convex dev",
"convex:auth-prepare": "node scripts/prepare-convex-auth.mjs",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"@ai-sdk/google": "4.0.101",
"@ai-sdk/openai": "4.0.98",
"@auth/core": "0.41.3",
"@convex-dev/auth": "0.0.96",
"@fontsource-variable/geist": "5.3.0",
"@fontsource-variable/geist-mono": "5.3.0",
"@modelcontextprotocol/sdk": "1.32.1",
"@radix-ui/react-alert-dialog": "1.1.15",
"@radix-ui/react-select": "2.2.6",
"@radix-ui/react-separator": "1.1.8",
"@radix-ui/react-slot": "1.2.4",
"@radix-ui/react-switch": "1.2.6",
"@tanstack/react-router": "1.170.45",
"@tanstack/react-start": "1.169.3",
"ai": "7.0.147",
"better-sqlite3": "13.0.3",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"convex": "1.47.0",
"cross-spawn": "7.0.6",
"epubjs": "0.3.93",
"framer-motion": "12.34.5",
"lucide-react": "0.576.0",
"motion": "12.35.1",
"openai": "6.27.0",
"react": "19.2.4",
"react-dom": "19.2.4",
"sonner": "2.0.7",
"tailwind-merge": "3.5.0",
"ts-fsrs": "5.4.2",
"zod": "4.6.7"
},
"devDependencies": {
"@eslint/js": "9.39.5",
"@tailwindcss/vite": "4.3.3",
"@tanstack/router-plugin": "1.168.47",
"@types/better-sqlite3": "7.6.13",
"@types/cross-spawn": "6.0.6",
"@types/node": "24.19.2",
"@types/react": "19.3.0",
"@types/react-dom": "19.3.0",
"@types/youtube": "0.3.0",
"@vitejs/plugin-react": "6.1.2",
"@xmldom/xmldom": "0.9.12",
"eslint": "9.39.5",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react-hooks": "7.1.1",
"fake-indexeddb": "6.2.6",
"globals": "17.13.0",
"happy-dom": "21.0.4",
"jszip": "3.10.2",
"tailwindcss": "4.3.3",
"typescript": "5.9.3",
"typescript-eslint": "8.72.0",
"vite": "8.3.4",
"vitest": "4.1.12"
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
39 changes: 19 additions & 20 deletions docs/syllo-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,44 @@

## Local mode

Use Node 22.12 or newer, npm, and a browser with IndexedDB. No Convex account is needed.
Use Node 22.12 or newer, pnpm, and a browser with IndexedDB. No Convex account is needed. Run these from the repo root:

```powershell
npm ci
npm run dev
pnpm install --frozen-lockfile
pnpm dev
```

Open `http://localhost:3000`. Leave `VITE_CONVEX_URL` blank. Home, Library, EPUB reading, notes, video bookmarks, Pomodoro, manual roadmaps, and reviews save on this browser. Videos still need an internet connection.

For a production build:

```powershell
npm run build
npm start
pnpm build
pnpm start
```

The production host listens on `127.0.0.1:3000` by default. Vite reads `.env.local` during development and builds. For server keys used by `npm start`, supply process environment variables or run `node --env-file=.env.local server.mjs`. Client deployment URLs are build-time settings, so rebuild after changing them.
The production host listens on `127.0.0.1:3000` by default. Vite reads `apps/web/.env.local` during development and builds. For server keys used by `pnpm start`, supply process environment variables or run `node --env-file=.env.local server.mjs` in `apps/web`. Client deployment URLs are build-time settings, so rebuild after changing them.

## Optional Convex sync

No deployment is configured in this checkout. The backend schema, owner-only password login, sync functions, and authenticated book downloads are prepared in `convex/`. Local mode remains available without them.
No deployment is configured in this checkout. The backend schema, owner-only password login, sync functions, and authenticated book downloads are prepared in `apps/web/convex/`. Local mode remains available without them. Run the commands in this section from `apps/web`.

1. Run `npm run convex:dev`. Sign in to Convex and choose a project and its development deployment. This writes deployment settings to your ignored `.env.local`. Keep this watcher running; an initial auth error can be resolved by configuring the following variables.
2. Run `npm run convex:auth-prepare` in a second terminal. It generates `.env.convex-auth.local` without printing keys or contacting Convex. It refuses to overwrite existing keys.
1. Run `pnpm convex:dev`. Sign in to Convex and choose a project and its development deployment. This writes deployment settings to your ignored `apps/web/.env.local`. Keep this watcher running; an initial auth error can be resolved by configuring the following variables.
2. Run `pnpm convex:auth-prepare` in a second terminal. It generates `.env.convex-auth.local` without printing keys or contacting Convex. It refuses to overwrite existing keys.
3. Apply those variables to your selected development deployment:

```powershell
npx convex env set --from-file .env.convex-auth.local
npx convex env set SYLLO_OWNER_EMAIL
npx convex env set SITE_URL http://localhost:3000
pnpm exec convex env set --from-file .env.convex-auth.local
pnpm exec convex env set SYLLO_OWNER_EMAIL
pnpm exec convex env set SITE_URL http://localhost:3000
```

Enter your email at the private terminal prompt. Only that address may create or use the account. Keep `.env.convex-auth.local` private and outside version control. Do not regenerate signing keys for an existing deployment unless you intend to rotate them.
4. Confirm `.env.local` has the public `VITE_CONVEX_URL` from the CLI. For the standard Convex domain, Syllo derives the HTTP origin by replacing `.convex.cloud` with `.convex.site`. Set `VITE_CONVEX_SITE_URL` explicitly if needed. Never put signing keys or deployment access tokens in a `VITE_` variable.
5. Restart `npm run dev`. In Settings, choose **First time? Create account**, use the configured owner email, and save your password in a password manager. This personal setup does not include email verification or password-reset delivery.
5. Restart `pnpm dev`. In Settings, choose **First time? Create account**, use the configured owner email, and save your password in a password manager. This personal setup does not include email verification or password-reset delivery.
6. In Settings, choose **Upload books from this device** for previously imported books. New imports upload when signed in. Reading positions, highlights, notes, kept review schedules, roadmaps, and timer preferences sync automatically. Downloads require sign-in and cache locally for later use.

The auth files follow [Convex Auth's manual setup](https://labs.convex.dev/auth/setup/manual); Syllo's schema and React provider are already installed. Avoid replacing `convex/auth.ts` with a generic setup template, which would remove the owner-email restriction.
The auth files follow [Convex Auth's manual setup](https://labs.convex.dev/auth/setup/manual); Syllo's schema and React provider are already installed. Avoid replacing `apps/web/convex/auth.ts` with a generic setup template, which would remove the owner-email restriction.

For a hosted origin, set Convex's `SITE_URL` to that exact origin and rebuild the app with its deployment URLs. The app's server APIs require the Convex sign-in token when connected. The local Scout SQLite database is a single-owner worker; it is not a multi-user backend.

Expand Down Expand Up @@ -84,14 +84,13 @@ Embedded YouTube playback uses YouTube's player. Syllo does not promise locked-p

## Coding agents and checks

`mcp/server.mjs` exposes `add_resource` and `search_library` over stdio against the same SQLite file. Settings shows connection snippets for installed coding agents. An MCP-added resource remains a candidate until approved.
`apps/web/mcp/server.mjs` exposes `add_resource` and `search_library` over stdio against the same SQLite file. Settings shows connection snippets for installed coding agents. An MCP-added resource remains a candidate until approved.

```powershell
npm run typecheck
npm run typecheck:convex
npm test
npm run lint
npm run build
pnpm typecheck
pnpm test
pnpm lint
pnpm build
```

Live Convex sync, provider calls, mobile installation, and playback need their respective accounts, devices, or network services. No cloud project is created by these checks.
Loading
Loading