# EdgeEver Project Structure ```text edgeever/ ├── apps/ │ ├── web/ │ │ ├── public/ │ │ │ ├── manifest.webmanifest │ │ │ └── icons/ │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── App.tsx │ │ │ │ └── routes.tsx │ │ │ ├── components/ │ │ │ │ ├── editor/ │ │ │ │ ├── layout/ │ │ │ │ ├── memo-list/ │ │ │ │ ├── notebook-tree/ │ │ │ │ └── ui/ │ │ │ ├── hooks/ │ │ │ ├── lib/ │ │ │ ├── styles/ │ │ │ └── main.tsx │ │ ├── index.html │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── api/ │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── env.ts │ │ │ ├── db/ │ │ │ ├── routes/ │ │ │ │ ├── memos.ts │ │ │ │ ├── notebooks.ts │ │ │ │ └── resources.ts │ │ │ └── services/ │ │ │ ├── merge-memos.ts │ │ │ └── resource-store.ts │ │ ├── package.json │ │ └── tsconfig.json │ └── site/ │ ├── public/ │ ├── src/ │ │ ├── components/ │ │ ├── content/ │ │ ├── layouts/ │ │ ├── pages/ │ │ └── styles/ │ ├── astro.config.mjs │ ├── package.json │ └── tsconfig.json ├── packages/ │ └── shared/ │ ├── src/ │ │ ├── content.ts │ │ ├── index.ts │ │ ├── schemas.ts │ │ └── types.ts │ ├── package.json │ └── tsconfig.json ├── migrations/ │ └── 0001_initial.sql ├── docs/ │ └── project-structure.md ├── tailwind.config.ts ├── wrangler.toml ├── bun.lock └── package.json ``` ## Deployment Shape EdgeEver should deploy as one Cloudflare Worker: - `/api/*` is routed to the Hono app first. - Static files from `apps/web/dist` are served by Workers Assets. - Unknown static routes fall back to `index.html` for SPA and PWA navigation. - `env.DB` is the D1 binding. - `env.RESOURCES` is the R2 bucket binding for images and attachments. The official website in `apps/site` is an Astro static site. It is built and deployed independently from the product Worker, typically to Cloudflare Pages. ## Frontend Boundaries - `components/layout` owns the responsive three-pane shell. - `components/notebook-tree` renders recursive notebooks from `parent_id`. - `components/memo-list` owns checkbox selection and merge action surfaces. - `components/editor` owns TipTap integration and Markdown serialization. - `lib/api-client.ts` should be the only browser module that talks to `/api/*`. ## API Boundaries - `routes/*` should stay thin: validate input, call services, return JSON. - `services/merge-memos.ts` owns the D1 transaction that creates a merged memo, soft deletes source memos, and re-points resources. - `services/resource-store.ts` owns R2 object keys and upload/download URL policy. - `db/*` owns SQL snippets and row mapping. ## Data Model Notes - Notebook nesting is unbounded through `notebooks.parent_id`. - Every memo belongs to exactly one notebook through `memos.notebook_id`. - Merge output is represented by a new memo with `source_memo_ids` and `merge_source_count`. - Merge inputs are retained as soft-deleted rows with `merged_into_memo_id`. - R2 objects are not moved during merge; `resources.memo_id` is updated to the new memo.