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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 10 additions & 6 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@
]
},
"overrides": [
{
"files": ["apps/web/src/hooks/use-mount-effect.ts"],
"rules": {
"no-restricted-imports": "off"
}
},
{
// Foldkit's own lint rules, scoped to the Foldkit app's source. The plugin is a devDependency
// of apps/web-foldkit (isolated linker), so it is loaded by path.
Expand Down Expand Up @@ -118,6 +112,16 @@
"rules": {
"foldkit/no-module-level-mutable-state": "off"
}
},
{
// Framework-free modules carried over from the React app: imperative singletons and
// helpers, not Foldkit programs, so the Foldkit architecture rules do not apply.
"files": ["apps/web-foldkit/src/core/**"],
"rules": {
"foldkit/no-impure-call-at-decision-time": "off",
"foldkit/no-module-level-mutable-state": "off",
"foldkit/no-switch-on-message-tag": "off"
}
}
],
"ignorePatterns": [
Expand Down
56 changes: 32 additions & 24 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

This is a monorepo with the following structure:

- `apps/web/` - React frontend using Vite, TanStack Router, and TailwindCSS
- `apps/web-foldkit/` - Web frontend built with Foldkit (Effect-based Elm architecture), Vite and TailwindCSS. Also the desktop app's webview
- `apps/backendv2/` - Backend API using Bun runtime and Effect-TS
- `apps/cluster/` - Effect Cluster service for distributed workflows and background jobs
- `packages/db/` - Shared database package: effect-orm table definitions, the `Database` service, and migrations
Expand Down Expand Up @@ -73,11 +73,12 @@ When working with Effect, Effect Atom, or TanStack DB, refer to these directorie
- `bun run test:once` - Run all tests once
- `bun run test:coverage` - Run tests with coverage report

### Web App (apps/web)
### Web App (apps/web-foldkit)

- `bun run dev` - Start Vite dev server on port 3000 (DO NOT USE - already running)
- `bun run build` - Build for production and typecheck
- `bun run dev` - Start Vite dev server on port 3100 (DO NOT USE - already running)
- `bun run build` - Build for production
- `bun run typecheck` - TypeScript checking without emitting files
- `bun run test` - Run the app's Vitest suite

### Backend (apps/backendv2)

Expand All @@ -100,15 +101,14 @@ When working with Effect, Effect Atom, or TanStack DB, refer to these directorie

### Frontend (Web App)

- **Framework**: React 19 with TypeScript
- **Framework**: Foldkit (Model / Message / update / view on Effect) with TypeScript
- **Build Tool**: Vite
- **Routing**: TanStack Router with file-based routing
- **Styling**: TailwindCSS v4 with Radix UI themes
- **UI Components**: React Aria Components + Ariakit
- **State Management**: TanStack Query + React Form
- **Rich Text**: Plate.js editor with AI features
- **Real-time**: Cloudflare Realtimekit
- **Auth**: Clerk (`@clerk/react`)
- **Routing**: Flat route union in `src/route.ts`
- **Styling**: TailwindCSS v4, `tailwind-variants` styles in `src/core/components/ui/*.styles.ts`
- **UI Components**: `src/ui/` on `@foldkit/ui`
- **Data**: TanStack DB collections synced through Electric (`src/core/db/collections.ts`)
- **Rich Text**: ProseMirror composer (`src/editor/`)
- **Auth**: Hazel auth (`@yielded/auth`)

### Backend

Expand Down Expand Up @@ -189,19 +189,27 @@ Uses effect-orm (`@maple-dev/effect-orm`, source in `~/Documents/GitHub/effect-c
```typescript
import { Database, PG, schema } from "@hazel/db"

const db = yield* Database.Database
const channel = yield* db.first(
PG.from(schema.channelsTable).select().where(($) => [$.id.eq(channelId), $.deletedAt.isNull()]),
)
yield* db.transaction(Effect.gen(function* () { /* every statement in here joins the transaction */ }))
const db = yield * Database.Database
const channel =
yield *
db.first(
PG.from(schema.channelsTable)
.select()
.where(($) => [$.id.eq(channelId), $.deletedAt.isNull()]),
)
yield *
db.transaction(
Effect.gen(function* () {
/* every statement in here joins the transaction */
}),
)
```

- `db.transaction(effect)` pins every statement inside `effect` (repositories included) to one connection through fiber context; there is no transaction handle to pass around.
- Schema changes: edit `src/tables`, then `bun run db:generate --name <name>`. Enums are `PG.enumType`, unique constraints `PG.unique` in `uniqueConstraints`; `generate` migrates both. What effect-orm does not model (check constraints, removing or reordering enum labels) goes in a `--custom` migration.

## Architecture Notes

- Frontend uses file-based routing with TanStack Router
- Backend follows Effect-TS patterns for error handling and dependency injection
- Real-time features implemented via Cloudflare Realtimekit
- Authentication handled by Clerk; backend verifies Clerk-issued JWTs and syncs users/orgs via webhook
Expand All @@ -214,7 +222,7 @@ yield* db.transaction(Effect.gen(function* () { /* every statement in here joins

### Electric SQL Proxy — Required When Adding New Collections

**IMPORTANT**: When adding a new Electric-synced collection on the frontend (`apps/web/src/db/collections.ts`), you **must also** update the Electric proxy to allow the table:
**IMPORTANT**: When adding a new Electric-synced collection on the frontend (`apps/web-foldkit/src/core/db/collections.ts`), you **must also** update the Electric proxy to allow the table:

1. **Add to `ALLOWED_TABLES`** in `apps/electric-proxy/src/tables/user-tables.ts`
2. **Add a `Match.when` case** in `getWhereClauseForTable` in the same file, using the appropriate WHERE clause builder (e.g., `buildOrgMembershipClause` for org-scoped tables, `buildChannelAccessClause` for channel-scoped tables)
Expand Down Expand Up @@ -341,7 +349,7 @@ yield *

## Brand Icons

Use Brandfetch CDN for integration brand logos/icons. See `apps/web/src/routes/_app/$orgSlug/settings/integrations/_data.ts` for the helper function.
Use Brandfetch CDN for integration brand logos/icons. See `apps/web-foldkit/src/core/lib/integrations/__data.ts` for the helper function.

**URL Pattern**: `https://cdn.brandfetch.io/{domain}/w/{size}/h/{size}/theme/{theme}/{type}`

Expand Down Expand Up @@ -548,10 +556,10 @@ Hazel Chat serves startup teams, developer teams, and enterprise teams who need

**Key design files:**

- Theme tokens: `apps/web/src/styles/theme.css`
- Tailwind config & animations: `apps/web/src/styles/styles.css`
- Theme presets: `apps/web/src/lib/theme/presets.ts`
- Component library: `apps/web/src/components/ui/`
- Theme tokens: `apps/web-foldkit/src/core/styles/theme.css`
- Tailwind config & animations: `apps/web-foldkit/src/core/styles/styles.css`
- Theme presets: `apps/web-foldkit/src/core/lib/theme/presets.ts`
- Component library: `apps/web-foldkit/src/ui/`

### Design Principles

Expand Down
2 changes: 1 addition & 1 deletion alchemy.run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import BotGateway from "./apps/bot-gateway/alchemy.run.ts"
import ElectricProxy from "./apps/electric-proxy/alchemy.run.ts"
import Landing from "./apps/landing/alchemy.run.ts"
import LinkPreview from "./apps/link-preview-worker/alchemy.run.ts"
import Web from "./apps/web/alchemy.run.ts"
import Web from "./apps/web-foldkit/alchemy.run.ts"

// Some secret stores define CLOUDFLARE_DEFAULT_ACCOUNT_ID; alchemy reads CLOUDFLARE_ACCOUNT_ID.
if (!process.env.CLOUDFLARE_ACCOUNT_ID && process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID) {
Expand Down
14 changes: 3 additions & 11 deletions apps/web/alchemy.run.ts → apps/web-foldkit/alchemy.run.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/**
* The web SPA, now the Foldkit app (`apps/web-foldkit`): `vite build` output served as Worker
* static assets (no Worker code).
* The web SPA: `vite build` output served as Worker static assets (no Worker code).
* `VITE_*` keys in `env` are build inputs: they reach `vite build` via the process env and are
* folded into the memo hash, so changing one rebuilds with no source change.
*/
Expand All @@ -13,8 +12,7 @@ const props = Effect.gen(function* () {
const { stage, domains, urls } = yield* HazelStack
return {
name: resolveWorkerName("web", stage),
cwd: new URL("../web-foldkit/", import.meta.url).pathname,
// `bun run build` also runs `tsc`; CI typechecks separately.
cwd: new URL("./", import.meta.url).pathname,
command: "bunx vite build",
outdir: "dist",
dev: { command: "bun run dev" },
Expand All @@ -24,13 +22,7 @@ const props = Effect.gen(function* () {
},
// Also hash the workspace sources the SPA bundles.
memo: {
include: [
"**/*",
"../web/src/**",
"../web/public/**",
"../../packages/*/src/**",
"../../libs/*/src/**",
],
include: ["**/*", "../../packages/*/src/**", "../../libs/*/src/**"],
lockfile: true,
},
compatibility: { date: "2026-10-01" },
Expand Down
18 changes: 14 additions & 4 deletions apps/web-foldkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,25 @@
"@electric-sql/client": "1.5.28",
"@foldkit/ui": "0.167.0",
"@fontsource/inter": "^5.2.8",
"@hazel/actors": "workspace:*",
"@hazel/auth": "workspace:*",
"@hazel/desktop": "workspace:*",
"@hazel/domain": "workspace:*",
"@hazel/effect-electric-db-collection": "workspace:*",
"@hazel/schema": "workspace:*",
"@simplewebauthn/browser": "^14.0.0",
"@tanstack/db": "0.11.3",
"@tanstack/hotkeys": "0.4.0",
"@yielded/auth": "0.1.0-beta.38",
"@yielded/auth-simplewebauthn": "0.1.0-beta.38",
"@yielded/crypto": "0.1.0-beta.38",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"effect": "catalog:effect",
"embla-carousel": "8.6.0",
"foldkit": "0.167.0",
"marked": "^17.0.4",
"prismjs": "^1.30.0",
"prosemirror-commands": "^1.7.2",
"prosemirror-history": "^1.5.1",
"prosemirror-keymap": "^1.2.3",
Expand All @@ -33,17 +42,18 @@
"prosemirror-transform": "^1.12.2",
"prosemirror-view": "^1.42.6",
"remend": "^1.2.2",
"rivetkit": "2.1.6",
"slate": "0.123.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"@yielded/auth": "0.1.0-beta.38",
"@yielded/auth-simplewebauthn": "0.1.0-beta.38",
"@yielded/crypto": "0.1.0-beta.38"
"tailwind-variants": "^3.2.2"
},
"devDependencies": {
"@foldkit/devtools": "0.167.0",
"@foldkit/oxlint-plugin": "0.15.3",
"@foldkit/vite-plugin": "0.27.0",
"@tailwindcss/vite": "^4.2.1",
"@types/prismjs": "^1.26.6",
"tailwindcss": "^4.2.1",
"tailwindcss-react-aria-components": "^2.0.1",
"tw-animate-css": "^1.4.0",
Expand Down
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ The notification system currently supports two production sounds:
To add new notification sounds to the app:

1. Add the MP3 file to this directory
2. Update the schema in `apps/web/src/hooks/use-notification-sound.tsx`
3. Add the option to the sound list in `apps/web/src/routes/_app/$orgSlug/settings/notifications.tsx`
2. Update the schema in `apps/web-foldkit/src/notification-sound.ts`
3. Add the option to the sound list in `apps/web-foldkit/src/page/my-settings/notifications/view.ts`
39 changes: 0 additions & 39 deletions apps/web-foldkit/scripts/generate-appearance-illustrations.ts

This file was deleted.

Loading
Loading