React + TypeScript, built with Vite and run with Bun. React is required because MDXEditor is a React component.
- Vite serves the frontend and the
/apiroutes, through a small plugin in the dev server. That's one process on one port, so onedor ensure -- bun run dev. - The server is long-lived. One instance serves every project and stays running in its own Dormouse pane.
dor ensurereuses it instead of starting a second one. We don't declare it as a Dor Tool. @monaco-editor/reactforDiffEditor, with Monaco bundled frommonaco-editorrather than the CDN it loads by default, so it works offline and in Selfie's renderer, which has no network.src/ui/monaco.tssets it up: every language, the language services' workers, and no diagnostics, since a file shown without its project can't resolve its imports.@mdxeditor/editorin read-only rich-text mode for markdown.picomatchfor category globs.diff(jsdiff) for the markdown merge.mdast-util-from-markdownto split markdown into blocks. MDXEditor already depends on it.- Storybook (
@storybook/react-vite), with Selfie for screenshot review. See Storybook. - Primer (
@primer/primitives,@primer/octicons-react,@primer/react). See Design. bun testfor unit tests.
vite.config.ts includes the /api plugin
.storybook/
src/
types.ts the data model, shared by server and UI
shared/ pure code, used by both the server and the UI
mdmerge.ts builds the merged markdown (tested)
classify.ts puts paths into categories (tested)
overview.ts groups files into DeltaOverview / CategoryDiff (tested)
fold.ts which markdown blocks to show, and which to fold (tested)
language.ts Monaco language ids by file extension
server/
api.ts Vite plugin with the /api routes
exec.ts run git / gh / dor, in parallel with a limit
origin.ts normalize remote URLs into RepoKeys (tested)
config.ts read ~/.pridm/categories.json and state.json; save pin and mute (tested)
prs.ts your open PRs, from one GitHub GraphQL query
discover.ts checkouts + PRs → BulkDelta[], and the join (tested)
surfaces.ts dor list → SurfaceRef[] per delta (not yet)
sources/
git.ts file list and contents from a local clone
github.ts file list and contents from the GitHub API
ui/
App.tsx the dashboard and delta pages
route.ts hash routing, with open categories in the URL (tested)
api.ts fetch from /api
theme.ts Primer setup, Monaco theme, light/dark
monaco.ts bundles the local monaco-editor and its workers
stories.ts dark twins, and waiting for diffs to draw
components/ CategoryCounts, CategorySection, CodeDiffView, Dashboard,
DeltaRow, DeltaView, ExpandBar, FileDiffView, FocusButtons,
MarkdownDiffView, PlusMinus
fixtures/ typed example data for stories
real/self.json pridm's own history, from its first commit to a pinned one, as a
delta; `bun run fixture:self`
scripts/
fixture-self.ts builds real/self.json with the server's git code
test/
| Route | Returns |
|---|---|
GET /api/deltas |
DeltaList: DeltaSummary[] plus warnings |
GET /api/deltas/:id |
DeltaOverview |
GET /api/deltas/:id/:category |
CategoryDiff |
PUT /api/focus/:id |
body {"focus": "pinned" | "normal" | "muted"}; saved in ~/.pridm/state.json |
Ids contain / and :, so they're URL-encoded.
- Each Monaco
DiffEditoris two editors, so an expanded category with many files gets heavy. Mount each one only when it scrolls into view. - Monaco needs an explicit height. Size each editor to its content.
- Monaco only takes
#rrggbbcolors, and a production build minifies Primer's#ffffffto#fff.theme.tsexpands them, or Monaco throws. - Monaco colors lines lazily, so a diff can paint before its colors.
CodeDiffViewwaits for the language's colors before mounting, colors every line at once, and is markeddata-diff-pendinguntil the diff is drawn. - MDXEditor needs its stylesheet imported (
@mdxeditor/editor/style.css) and a plugin for every kind of block it should render: headings, lists, quotes, tables, code blocks, links, thematic breaks, frontmatter.