Appearance
Folder Structure
The website repo (ediky-website) at a glance. The app itself lives one level down in ediky/ — CI's working-directory is set accordingly, which matters when editing workflows.
ediky-website/
├── .github/workflows/
│ ├── deploy.yml # workflow_dispatch → reusable CF Pages deploy (ci-infra)
│ └── ci.yml # lint + build on push/PR
├── worker/ # background OA judging Worker (own wrangler.toml)
│ └── src/index.js
├── worker-reminders/ # per-minute reminder cron Worker
│ └── src/index.js
├── firestore-rules/
│ ├── firestore.rules
│ └── storage.rules
└── ediky/ # ← the app root
├── public/
│ ├── content/ # generated question bodies + .tests.json sidecars
│ ├── content-index/ # generated per-section index shards + manifest.json
│ ├── favicon.svg logo.png … # brand assets (shared with this docs site)
│ └── _headers # cache-control fix for stale SPA deploys
├── functions/ # Cloudflare Pages Functions (the API)
│ ├── _ai-providers.js # shared 9-provider fallback chain
│ ├── ai-code-review.js # help / chat / ask / optimize modes
│ ├── grade-concept.js evaluate-aptitude.js grade-sql-practice.js
│ ├── run-code.js # Judge0 proxy
│ ├── get-notes.js list-notes.js # private-Docy proxy (GITHUB_TOKEN)
│ └── cf-profile.js cf-status.js # Codeforces proxy
├── src/
│ ├── App.jsx main.jsx
│ ├── components/
│ │ ├── layout/ # MainLayout, Navbar, Sidebar, UserMenu
│ │ ├── bank/ coding/ oa/ ide/ calendar/ learning/ revision/ dashboard/
│ │ └── ui/ # primitives (buttons, dialogs, popovers)
│ ├── pages/ # route components (+ bank/ cp/ oa/ subjects/ subdirs)
│ ├── context/ # Auth / Theme / Sidebar / Submissions
│ ├── hooks/ # 20 use* hooks
│ ├── lib/ # all non-UI logic (questionBank, judgeRunner, oaEngine, …)
│ ├── content/ # SOURCE question markdown (input to build-content)
│ ├── data/ # static data (cpTests.js, companies, playlists)
│ └── styles/theme.css # the CSS-variable token system (light + dark)
├── tools/
│ ├── build-content.mjs # the sharder — see Question Bank
│ ├── lint-content.mjs # wired into npm run build; bans bits/stdc++.h etc.
│ ├── convert-sheet.mjs gen-cp-content.mjs
│ └── copy-sql-wasm.mjs # ships sql.js WASM into public/
├── scripts/send-reminders.mjs backfill-emails.mjs
├── .env.example # VITE_FIREBASE_*, VITE_CLOUDINARY_*, VITE_DOCS_URL
├── vite.config.js eslint.config.js package.json
└── index.htmlConventions that keep it navigable
src/content/is input,public/content*is output. Never hand-edit anything underpublic/content— it's regenerated on every build. Authoring happens in the tools repo; the built shards are what ships.- Generated vs committed:
public/content-index/andpublic/content/are build artifacts ofbuild-content.mjs. The source of truth issrc/content/markdown. lib/has no JSX;components/has no fetch calls. The seam between them is the hooks directory. This is what made the annotation-system rewrite and the judging rebuild possible without touching page markup.- One parser (
contentParse.js), one AI client (aiService.js), one judging path (judgeRunner.jsbehindrun-code). Duplicates of these have each caused a real bug historically; the singletons are load-bearing. - Workers are separate deployables.
worker/andworker-reminders/have their ownwrangler.tomland deploy lifecycle — a website deploy never touches them and vice versa. - Functions prefixed
_(like_ai-providers.js) are shared modules, not routes — Cloudflare skips underscore files when generating endpoints.