No description
- TypeScript 99.5%
- CSS 0.3%
- Dockerfile 0.1%
timetables (singleton): per-school list with status filter (draft /
active / archived), search by name + description.
timetable-detail (hidden keyed): header with activate/archive/delete
lifecycle actions, editable header form on drafts, Mon-Fri × periods
grid with click-cell to add/edit/delete entries (class, teacher, room,
notes). Reads periods from attendance + classes from directory for
the pickers.
Spec sync: brings in TimetablesTimetableList / EntryList /
*Schedule envelope shapes from backend
|
||
|---|---|---|
| .forgejo/workflows | ||
| deploy/k8s | ||
| openapi | ||
| src | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| Dockerfile | ||
| eslint.config.js | ||
| index.html | ||
| nginx.conf | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.app.json | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
synthrik / frontend
The Synthrik schools-platform frontend. One shell, role-gated app launcher, windowed-desktop UI.
Stack
- Vite + React 19 + TypeScript (strict)
- Tailwind v4 + a small set of in-repo primitives (
src/ui/) - Zustand for window-manager state
- TanStack Query for server state (wired later)
- TanStack Router for routing (wired later)
- Custom pointer-events window manager — no
react-rnd, noreact-draggable
Layout
src/
├── main.tsx, app.tsx # bootstrap + providers
├── ui/ # primitives (Button, Icon, …)
├── shell/ # the desktop chrome
│ ├── canvas.tsx, topbar.tsx, appbar.tsx, window.tsx, grid.tsx
│ ├── registry.tsx # registered apps
│ └── wm/ # window manager (zustand store + drag/resize/snap)
├── apps/ # each bolt-on is a self-contained tree
│ └── hello/ # the smoke-test app
├── lib/ # env, utils, api clients (later)
└── styles/globals.css
Each "app" registers itself with appRegistry from src/shell/registry.tsx.
The app bar's launcher shows whatever has been registered; clicking spawns a
window with the app's component as the body.
Run
pnpm install
pnpm dev # http://localhost:5173
pnpm typecheck # tsc -b --noEmit
pnpm lint # eslint .
pnpm build # tsc -b && vite build
Window manager — quick reference
- Drag: pointerdown on the window header. Drop near a screen edge to snap (left/right half, top/bottom half, or a corner quadrant).
- Resize: pointerdown on a window edge or corner. Min size enforced.
- Header icons (right to left): close · maximise/restore · grid-snap-cycle (walks through halves + quadrants) · minimise.
- Snap grid: toggle with the "grid" button in the top bar — when on,
drag and resize round to
gridSize(16 px default). - Double-click header: maximise / restore.
- App bar:
- Left section: installed-app launchers (click to spawn).
- Right section: per-window pills (click = focus, click focused = minimise, click minimised = restore + focus).
Adding a new app
// src/apps/attendance/index.tsx
import { Icon } from "@/ui/icon";
function AttendanceApp({ windowId }: { windowId: string }) {
return <div>...</div>;
}
export const attendanceApp = {
id: "attendance",
name: "Attendance",
icon: Icon.Spawn, // pick or add one
defaultSize: { w: 720, h: 480 },
component: AttendanceApp,
};
Then register it in src/apps/index.ts:
import { attendanceApp } from "./attendance";
appRegistry.register(attendanceApp);