Inspiration
Revision the night before an exam turns into tab management. You open the lesson, then a search for the topic you did not follow, then four YouTube videos that are either a full course or a thirty second short, then a chat window where you paste the whole chapter and hope for the best. Nothing you find stays attached to the thing you were revising, so the next evening you find it all again.
Dock started from one idea. The unit of revision is not a subject and not a course. It is a single lesson, and the topics inside it. If the workspace is scoped that tightly, everything in it can be scoped too. The chat knows which topic you are on. The videos belong to that topic. The syllabus section tells both how deep to go.
What it does
You create a space for one lesson and share the syllabus section it covers. The space opens as a grid of topic cards with the lesson at the centre.
Each card gives you two things.
Learn mode is a chat scoped to that one topic. It stays inside the topic and answers at the depth the syllabus asks for rather than dumping a textbook on you. Every prompt carries one rolling summary of the older conversation plus the last ten messages, so a long session never grows past the model's input budget.
The video shelf is up to twenty YouTube explainers matched to that topic, five per request, playing inside the panel so revising does not mean leaving for a page full of recommendations. The shelf deliberately mixes Indian teaching channels with international English ones, because a student in India revising for boards or GATE is served by both and a plain search usually surfaces only one.
Learn mode runs on your own Gemini key. You paste it once, it is never shown back, and the app only ever tells you whether one is set.
How we built it
The frontend is Next.js on the App Router with Tailwind, TanStack Query for the data layer and Zustand for auth state. The backend is FastAPI on Python 3.14 with Poetry, MongoDB through the async pymongo client, PyJWT and bcrypt.
Two rules shaped everything.
Next.js never proxies the API. No route handlers, no server side fetching, no server actions for data. The browser calls FastAPI directly through one axios instance. That keeps exactly one place where a request is made and one place where an error is normalised.
The backend owns the session. It issues short lived access tokens and long lived refresh tokens as httpOnly cookies, and the frontend stores nothing at all. No localStorage, no token in JavaScript. Refresh tokens are stored hashed so they can be rotated and revoked, every refresh rotates the pair, and replaying an already rotated token is treated as a leak and kills every session for that user.
Layering is strict on both sides. On the backend it is router to service to DAO, where the router never writes a query and the service never raises an HTTP error. On the frontend it is component to hook to api module to axios. Skipping a layer is treated as a bug rather than a shortcut.
The canvas pans and zooms with one CSS transform on one wrapper, and the cards reach it as children, so panning never re renders a single card. No canvas element and no graph library.
Challenges we ran into
The videos were mostly fake. The first version asked the model to recommend explainers and it answered with confident, perfectly formed YouTube URLs whose ids belonged to no video at all. We added verification against YouTube's oEmbed endpoint, which correctly threw the dead ones away, and that is when the real problem showed up. Asking for five reliably produced one or two survivors. The model was not being unhelpful. It simply cannot know an eleven character video id from memory.
So we inverted it. The model no longe
Log in or sign up for Devpost to join the conversation.