π§ Context
The Explorer (src/pages/Explorer.tsx) renders the full prerequisite DAG β every course in courseList becomes a node, laid out by dagre in computeLayout. With the current data that's ~25 nodes with heavy edge fan-out, which reads as an overwhelming wall on first load (nothing is selected yet, so everything is shown). With the full set of courses in the future, it'll be even denser.
This ticket makes the default view show only the core CS-program courses, with a toggle to opt into the full (overwhelming, but deliberate) graph. The data is already in place:
courses.json carries a core?: boolean flag on each course.
src/data/loadCourses.ts exports coreCourseList (the core courses) alongside courseList, courses, and prereqEdges.
So: default = coreCourseList; "Show all" = courseList.
The Explorer already drops edges whose endpoints aren't both rendered (visibleEdges / knownCodes in Explorer.tsx). This ticket generalises that: the visible course set becomes core-only by default, and edge filtering follows from it.
Architecture (keep this seam)
This is the first feature that renders a subset of the graph, so set the pattern up cleanly β later filter modes (reachable-on-click, department/year, search) and edge features will build on it:
- Filtering lives outside layout. Compute the visible course set first, then hand it to
computeLayout. computeLayout stays pure over its inputs β it lays out whatever nodes/edges it's given and must not read the toggle itself. (This also keeps the layout engine swappable in a later ticket without touching filter code.)
- One "visible course set" is the single source of truth for what's shown. Edge filtering derives from it (an edge shows iff both endpoints are in the set). Expose/structure it so a later ticket can derive its own visibility from the same set β e.g. a synthetic gateway node, or a new kind of edge, shows only when its related course is visible. Don't hardcode "core vs all" anywhere except where the set is chosen.
- Selection behaviour is unchanged. Clicking a node still selects + highlights/dims via the existing
explorerStore; this ticket only changes which courses are in the graph and adds the toggle. Rebuilding the graph to a clicked course's reachable set is a separate, later ticket β do not do it here.
Watch out: "fake roots"
In core-only mode, a core course whose prerequisites are all non-core will have those edges dropped and render as if it has no prerequisites (a misleading "fake root"). The current core set was checked and is clean β every core course has at least one visible core prerequisite, and its non-core prereqs aren't graph nodes anyway, but keep this property in mind.
π οΈ Implementation Plan
Note: While working on this if you realize this draft plan isn't fully accurate, feel free to adjust as needed to achieve the desired functionality, and flag it in the PR description.
- Add toggle state to
explorerStore β e.g. showAllCourses: boolean (default false) plus a setter. This is UI state only, consistent with the store's existing role (it holds selection/highlight state, never course data).
- Derive the visible course set in the Explorer from the flag:
showAllCourses ? courseList : coreCourseList. Keep this derivation outside computeLayout.
- Filter edges to those whose
from and to are both in the visible set (generalise the existing knownCodes / visibleEdges logic to use the visible set instead of the full courseList).
- Pass the visible nodes + filtered edges into
computeLayout, and recompute layout when the visible set changes (the layout useMemo must now depend on the toggle / visible set, not run once). Keep computeLayout pure over its inputs.
- Add a toggle control inside the Explorer view (an overlay in a corner of the graph area) β not in the shared
Header, which renders on both routes. The label should reflect the current mode (e.g. "Show all courses" when core-only; "Show core only" when showing all). Let React Flow fitView re-fit after the set changes so the new view is framed.
- Leave click-to-select highlight/dim behaviour as-is; it should keep working within whatever set is currently visible.
- Run
pnpm dev and confirm: core-only on load, toggle swaps to the full graph and back, no dangling edges, selection still highlights. Then pnpm typecheck, pnpm lint, pnpm format, pnpm test.
β
Acceptance Criteria
π§ Context
The Explorer (
src/pages/Explorer.tsx) renders the full prerequisite DAG β every course incourseListbecomes a node, laid out by dagre incomputeLayout. With the current data that's ~25 nodes with heavy edge fan-out, which reads as an overwhelming wall on first load (nothing is selected yet, so everything is shown). With the full set of courses in the future, it'll be even denser.This ticket makes the default view show only the core CS-program courses, with a toggle to opt into the full (overwhelming, but deliberate) graph. The data is already in place:
courses.jsoncarries acore?: booleanflag on each course.src/data/loadCourses.tsexportscoreCourseList(the core courses) alongsidecourseList,courses, andprereqEdges.So: default =
coreCourseList; "Show all" =courseList.The Explorer already drops edges whose endpoints aren't both rendered (
visibleEdges/knownCodesinExplorer.tsx). This ticket generalises that: the visible course set becomes core-only by default, and edge filtering follows from it.Architecture (keep this seam)
This is the first feature that renders a subset of the graph, so set the pattern up cleanly β later filter modes (reachable-on-click, department/year, search) and edge features will build on it:
computeLayout.computeLayoutstays pure over its inputs β it lays out whatever nodes/edges it's given and must not read the toggle itself. (This also keeps the layout engine swappable in a later ticket without touching filter code.)explorerStore; this ticket only changes which courses are in the graph and adds the toggle. Rebuilding the graph to a clicked course's reachable set is a separate, later ticket β do not do it here.Watch out: "fake roots"
In core-only mode, a core course whose prerequisites are all non-core will have those edges dropped and render as if it has no prerequisites (a misleading "fake root"). The current core set was checked and is clean β every core course has at least one visible core prerequisite, and its non-core prereqs aren't graph nodes anyway, but keep this property in mind.
π οΈ Implementation Plan
Note: While working on this if you realize this draft plan isn't fully accurate, feel free to adjust as needed to achieve the desired functionality, and flag it in the PR description.
explorerStoreβ e.g.showAllCourses: boolean(defaultfalse) plus a setter. This is UI state only, consistent with the store's existing role (it holds selection/highlight state, never course data).showAllCourses ? courseList : coreCourseList. Keep this derivation outsidecomputeLayout.fromandtoare both in the visible set (generalise the existingknownCodes/visibleEdgeslogic to use the visible set instead of the fullcourseList).computeLayout, and recompute layout when the visible set changes (the layoutuseMemomust now depend on the toggle / visible set, not run once). KeepcomputeLayoutpure over its inputs.Header, which renders on both routes. The label should reflect the current mode (e.g. "Show all courses" when core-only; "Show core only" when showing all). Let React FlowfitViewre-fit after the set changes so the new view is framed.pnpm devand confirm: core-only on load, toggle swaps to the full graph and back, no dangling edges, selection still highlights. Thenpnpm typecheck,pnpm lint,pnpm format,pnpm test.β Acceptance Criteria
coreCourseList), not the full graphHeader) switches between core-only and all courses, with a label that reflects the current modecomputeLayout;computeLayoutstays pure over its inputs and does not read the togglepnpm typecheck,pnpm lint,pnpm format:check, andpnpm testpass