WallpaperMachine
당신이 사랑하는 배경화면, 맥에서 새롭게 만나보세요
저장소 정보는 아직 수집하지 않았습니다.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Small daily steps, compounding growth.
Compound is a web app for getting 1% better every day. You track daily habits, follow short AI-generated learning tracks picked for you, break big goals into steps you can take today, keep in touch with the people in your network, see your Google Calendar next to it all, jot down quick notes, and write down every peso you spend.
Live: compound-nine-chi.vercel.app
This is my portfolio project, built in small, deployable steps.
| Module | What it does | Status |
|---|---|---|
| Learning & Habits | Daily habits with streaks, and a missed day from the last week can be filled in. AI learning tracks (skills, reading plans, thinking exercises) with lessons, exercises and feedback, at your own pace: finishing a day opens the next one. A principle of the day. | Done |
| Goals | The AI drafts a plan of milestones and steps, which you can edit. Each goal keeps buffer days before its deadline. When you fall behind, the remaining dates move and the status changes from on track to behind or at risk. Goals that need attention get a dot in the nav. | Done |
| About you | A short profile (what you do, what you want to get better at, what you know, time per day) that personalises principles, track plans and goal plans, and powers "Recommended for you" tracks. It can be filled in from a resume PDF, which is never stored. | Done |
| Contacts | People you meet for work: where you met, a follow-up date and a related goal. Log touchpoints, see who to follow up with on Today, and see who can help on each goal. | Done |
| Schedule | Connect Google Calendar (read-only) to see today's events on Today and the week ahead on a Schedule page. Events are fetched live and never stored. | Done |
| Notes | Quick notes and to-dos on a board of sticky notes: add one from Today in a second, tick it off, pin what matters, pick a paper colour. | Done |
| Money | Record expenses and income in a few taps. Monthly totals, spending by category, and your own categories. Income can go toward a goal's savings target. | Done |
| A goal that's slipping | Money for the month |
|---|---|
| Recommended for you | About you, with "fill in from your resume" |
| A contact, with touchpoints | The week from Google Calendar |
| Sticky notes | Habits, with the last week to fill in |
| Progress | Landing page |
<Suspense> and streams in, so pages appear instantly.proxy.ts sends signed-out visitors to /login, but the real check lives in lib/auth/dal.ts. Every protected page and server action calls requireUser() or requireOnboardedUser() before it touches data.lib/db/types.ts, so Kysely catches mistakes in queries at compile time. Constraints such as allowed values and unique check-ins are enforced in Postgres as well.lib/habits/fill-in.ts), shared by the page and the server action: only the last 7 days, only days the habit was due, and never a habit that finishing a lesson checks off.AI_DEMO_MODE=1), the app uses clearly labelled example content instead of calling Claude, so it runs end to end for free.lib/goals/schedule.ts works out the status, adjusted dates and buffer use from the plan and the steps you've ticked. Nothing about the schedule is stored, and it's covered by unit tests. The pace comes from the original plan, so extending a deadline really gives you more room.You need Node.js 20.9 or newer, a Neon database, and a Google OAuth client and/or a GitHub OAuth app.
git clone https://github.com/DanielZyrick/Compound.git
cd Compound/compound
npm install
cp .env.example .env.local
Use a separate Neon branch for development, so local work never touches production data:
dev from main. Set auto-delete to never.dev branch, and copy two connection strings into .env.local:
-pooler) into DATABASE_URLDATABASE_URL_UNPOOLEDDon't run vercel env pull: it would overwrite .env.local with the Vercel project's variables.
Generate AUTH_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Google. In the Google Cloud console, create an OAuth client ID of type Web application with these authorized redirect URIs:
http://localhost:3000/api/auth/callback/googlehttps://<your-domain>/api/auth/callback/googlePut its client ID and secret in AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET. One Google client can serve both local and production.
GitHub. Create an OAuth app at github.com/settings/developers with these settings:
http://localhost:3000http://localhost:3000/api/auth/callback/githubPut its client ID and secret in AUTH_GITHUB_ID and AUTH_GITHUB_SECRET. Production uses a separate GitHub OAuth app, because a GitHub OAuth app has only one callback URL.
Accounts are matched by email, so Google and GitHub open the same Compound account when they share an address. Sign-in only accepts an address the provider has verified.
Google Calendar (optional). The same Google client also serves Calendar. In the same Google Cloud project:
.../auth/calendar.readonly scope to the consent screen. It's a sensitive scope: until Google verifies the app, people who connect see an "unverified app" warning.http://localhost:3000/api/calendar/callbackhttps://<your-domain>/api/calendar/callbackCALENDAR_TOKEN_KEY to 32 random bytes in base64 (the same command as AUTH_SECRET above), with a different key in each environment.npm run db:migrate
npm run dev
Open localhost:3000 and sign in with Google or GitHub.
Run these from the compound/ folder.
| Command | What it does |
|---|---|
npm run dev | Start the dev server |
npm run build / npm start | Production build and server |
npm test / npm run test:watch | Run the Vitest unit tests |
npm run test:e2e | Build, then run the Playwright end-to-end tests (desktop and phone) |
npm run typecheck | Type-check with tsc |
npm run lint | Run ESLint |
npm run db:migrate | Apply pending migrations |
npm run db:rollback | Undo the most recent migration |
db:migrate uses DATABASE_URL_UNPOOLED from .env.local. To migrate a different branch, put that branch's direct connection string in a temporary, gitignored env file and pass it to the runner. Variables from the file take priority over .env.local:
# .env.migrate-main contains: DATABASE_URL_UNPOOLED=<direct connection string>
npx tsx --env-file=.env.migrate-main db/migrate.ts up
rm .env.migrate-main
The 28 Playwright tests in e2e/ sign in as a dedicated test user and walk through onboarding, habits (including filling in a missed day), lessons at your own pace, a reading, goals (creating, editing steps, falling behind and catching up), money (adding, editing, categories, saving toward a goal), notes, About you (including a resume), recommended tracks, contacts and follow-ups, Google Calendar, and settings. Then they delete the test user. Each test runs on desktop and on a phone. They:
DATABASE_URL points at the host in E2E_DB_HOST, so they can't touch production;AI_DEMO_MODE=1), so they never spend API credits;CALENDAR_DEMO_MODE=1), so they never call Google;Running npm start locally also needs AUTH_TRUST_HOST=true. Vercel sets it automatically, and npm run dev doesn't need it.
The app is deployed on Vercel with the root directory set to compound. Production uses the Neon main branch.
When a change includes a new migration:
main, using the command above.main. Vercel builds and deploys it.Migrations add to the schema before the code that uses it ships. A migration that renames or removes something can break the live site until the new code is deployed, so plan those deploys separately.
| Variable | Used for |
|---|---|
DATABASE_URL | Pooled Postgres connection (the app) |
DATABASE_URL_UNPOOLED | Direct Postgres connection (migrations) |
AUTH_SECRET | Encrypts session cookies |
AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET | Google OAuth client |
AUTH_GITHUB_ID, AUTH_GITHUB_SECRET | GitHub OAuth app |
ANTHROPIC_API_KEY | Claude API (server only) |
MODEL_FAST, MODEL_SMART | Claude model names: fast for feedback, smart for planning |
AI_DEMO_MODE | Optional. 1 uses example content instead of calling Claude |
CALENDAR_TOKEN_KEY | Optional. Encrypts Google Calendar tokens (32 bytes, base64); without it, Calendar isn't offered |
CALENDAR_DEMO_MODE | End-to-end tests only. 1 uses a demo calendar instead of calling Google |
E2E_DB_HOST | End-to-end tests only: the dev database host they're allowed to use |
.env.local is gitignored. Only .env.example is committed, with empty values.
docs/screenshots/ images for this README
compound/
app/
(app)/ signed-in pages: today, schedule, habits, tracks, goals, money, notes, contacts, progress, profile, settings
api/calendar/ Google Calendar connect and callback routes
onboarding/ first-run setup: focus areas, first habits, timezone
login/ Google and GitHub sign-in, error messages
page.tsx landing page (fully static)
components/
app-shell/ sidebar, phone tab bar, account menu, nav attention dot
landing/ landing page sections
money/ category icons
ui/ shadcn/ui components
db/
migrations/ Kysely migrations, listed in index.ts
migrate.ts migration runner (npm run db:migrate)
e2e/ Playwright tests and their helpers
lib/
ai/ Claude client, demo mode, daily limits, call logging
auth/ data access layer, server actions, login errors
calendar/ Google OAuth, token encryption, fetching events, days in your timezone
contacts/ contact queries, follow-up dates
db/ Kysely client and table types
goals/ plan prompt, schedule engine, queries
habits/ streaks, check-ins, the week strip, filling in missed days
money/ amounts in cents, categories, monthly summaries
notes/ note queries, paper colours
profile/ About you, the learner context for prompts, reading a resume
recommendations/ "Recommended for you" tracks
tracks/, lessons/, reading/, principles/, progress/
validation/ Zod schemas
auth.ts Auth.js setup
proxy.ts redirects signed-out visitors to /login
Later: monthly budgets, due dates on to-dos, email reminders, AI feedback evals.
Built by Daniel Zyrick Gayao.
IntlgetLearnerContext combines your focus areas and About you answers into a few plain lines. Principles, track plans, goal plans and recommendations all use it, inside tags the prompts treat only as a description. A resume PDF is sent to Claude once to draft the answers, then dropped; contact details are left out.fetch. A random state cookie protects the connect flow, tokens are encrypted with AES-256-GCM before they're stored (lib/calendar/crypto.ts), and events are fetched live, so the database never holds anyone's calendar. Each event lands on the right day in your timezone, including on daylight-saving days.