T-KQ7W-note-pages-in-sdf
State: planning/draft · Impact: medium · Complexity: medium
M-YX34’s Notes deliverable is “Routes for the note kind and the N prefix, with wikilinks
resolved and task lists rendered”. A note must open as a page in SDF, listed by genre, rendered
as markdown, with its wikilinks going to real pages and its checklists showing their state. The
acceptance target is N-YQVC-master-consolidation, the master checklist. Every screen reads
the real API (T-1ODN); the views are read-only.
- Pages under
solutions/ontological/desktop/src-nuxt/app/pages/model/:tasks.vue,milestones.vue, and the generic browserentities/[type].vue(list in the secondary pane, viauseScopedEntityList) withentities/[type]/[id].vue(detail viauseEntityDetailinapp/composables/useEntities.ts). - The front-end roster
BROWSABLE_ENTITY_TYPESinapp/utils/entityRegistry.ts(L25-35) has no notes.resolveEntityLinkbuilds on that roster (BY_PREFIX), soNids resolve to null today.Calready resolves to/model/entities/capabilities/<id>(pinned by the “links a capability wikilink” test inentityRegistry.test.ts), so the doc comment aboveresolveEntityLink(“C… -> null, gated on M-0012”) is stale and must be fixed in this task. - Body rendering:
app/components/entities/EntityBodySection.vuemountsMdViewerwithentityBodyRenderOptionsandentityBodyComponentsfromapp/utils/entityBodyRenderOptions.ts. ThereresolveLink: (target) => ({ kind: 'missing', display: target })marks every wikilink missing (nohref, classwikilink-missing). The resolver is module-level and stable, so it cannot see the document’s project. - Task lists:
entityBodyComponentsmaps'input.checkbox'toReadOnlyTaskCheckbox, which renders a disabled<input type="checkbox">whosecheckedcomes from the node. So the state displays and clicking does nothing. Task-list CSS is inpackages/ts/obsidian-md/src/theme.css. EntityBodySection.vueoffers an Edit mode when asaveis passed (canEdit). The note page must not offer it.- The note schema (
lib/model/entities/note/schema.ts) hasgenre(8 values) and optionalparent(a note wikilink). The list rows get both fields fromT-1ODN. N-YQVChasgenre: plan, 58[[wikilinks, and- [x]task lines.- Resolver outcomes available from
@sksizer/obsidian-md(LinkResolution.kindinpackages/ts/obsidian-md/src/types.ts):note,attachment,missing,external. There is no “plain text” kind.
Proposed
Section titled “Proposed”Notes open at /model/entities/notes and /model/entities/notes/<id> through the generic
browser. The list groups rows by genre. The detail renders the body with wikilinks that go to the
right page (carrying ?project=), checkboxes that show their state, and a parent link to the
hub note. N-YQVC renders fully.
Approach
Section titled “Approach”- Add
{ type: 'notes', prefix: 'N', label: 'Notes' }toBROWSABLE_ENTITY_TYPESinentityRegistry.ts. This makesNresolve inresolveEntityLinkandparseWikiText, so theparentandrelatedfrontmatter chips link too. Fix the staleCcomment. This task owns that comment fix;T-14JAedits the same file’s neighbours but leaves the comment to this task and fixes only thefixtures.tsheader. The Model nav is generated fromBROWSABLE_ENTITY_TYPES(plugins/register.ts), so this entry also adds a Notes nav entry. - Add a pure helper
groupByGenre(rows)(inentityRegistry.tsor a new util with a test): rows grouped bygenre, genres inNOTE_GENRESorder, null genre last, rows in id order. - Render the grouping in the list for
notesonly (a section per genre insideEntityListPanelor[type].vue); other types keep their flat list. - Make body links resolve. Replace the module-level resolver with a factory
entityBodyRenderOptionsFor(project)that is memoized per project id, so theoptionsidentity stays stable. It callsresolveEntityLink(target, project): a route gives{ kind: 'note', path: route, display: target }; null gives{ kind: 'missing', ... }.entityBodyProvider.resolveLinkmust use the same function (its doc says they share one). Decision: an unknown kind (for exampleglossary) keeps the dead look (missing); no plain-text kind or CSS rule is added. - Navigate inside the app.
WikilinkAnchor(packages/ts/obsidian-md/src/vue/components/) callspreventDefaulton a plain click and reports it aslink-click, whichMdViewerre-emits as@link-clickwith{ resolution, event }; a resolvedhrefalone does not navigate. SoEntityBodySection.vueadds@link-clickonMdViewer: whenresolution.kind === 'note'andresolution.pathis set, callnavigateTo(resolution.path)(router push, no reload). Modified clicks fall through to the browser. - Task checkboxes need no new code.
entityBodyComponentsalready maps'input.checkbox'toReadOnlyTaskCheckbox, which readscheckedfrom the node, and that path already renders task bodies.N-YQVChas 59- [ ]and- [x]lines. The live check (AC-6) is the verification; if it shows a box missing, fixReadOnlyTaskCheckboxin that same PR. - Pass no
savefor notes in[type]/[id].vueso no Edit button shows. parentshows as a link through the existing frontmatter table: it renders wikilink chips throughparseWikiText, which resolves them onceNis on the roster (step 1). No separate header link is added; AC-8 checks it.- Vitest cases for
resolveEntityLink('N-YQVC', 'p1'), the resolver factory,groupByGenre.
| Area | Note |
|---|---|
solutions/ontological/desktop/src-nuxt/app/utils/entityRegistry.ts | Roster, N link, genre helper |
solutions/ontological/desktop/src-nuxt/app/utils/entityRegistry.test.ts | Pure-helper tests |
solutions/ontological/desktop/src-nuxt/app/utils/entityBodyRenderOptions.ts | Project-aware resolver, task boxes |
solutions/ontological/desktop/src-nuxt/app/components/entities/EntityBodySection.vue | Pass the resolver options |
solutions/ontological/desktop/src-nuxt/app/components/entities/EntityListPanel.vue | Genre sections |
solutions/ontological/desktop/src-nuxt/app/pages/model/entities/[type].vue | List wiring |
solutions/ontological/desktop/src-nuxt/app/pages/model/entities/[type]/[id].vue | Read-only note detail |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
resolveEntityLink('N-YQVC', 'p1')returns/model/entities/notes/N-YQVC?project=p1; vitest. - AC-2:
groupByGenreorders groups byNOTE_GENRES, puts null genre last, and keeps id order inside a group; vitest. - AC-3: The body resolver returns a
noteresolution with the project-carrying route forN,D,T,M,Ctargets, andmissingfor an unknown kind; vitest. - AC-4: The resolver factory returns the same object for the same project id; vitest.
- AC-5: Against
sdf-serve,/model/entities/noteslists notes grouped by genre, and the Model nav shows a Notes entry. - AC-6: Against
sdf-serve, theN-YQVCpage shows its- [x]and- [ ]items as disabled checkboxes with the right state, and clicking one changes nothing and sends no request. - AC-7: On that page a
N-...link opens the other note without a page reload, with?project=kept on the URL. - AC-8: A note with a
parentlinks to it from the frontmatter table; no Edit button shows on a note. - AC-9: Styles use only
--d-*tokens; no raw colour values are added. - AC-10: The
resolveEntityLinkdoc comment inentityRegistry.tsno longer saysC… → null.
Verification
Section titled “Verification”CI is the typecheck; vue-tsc is never run locally (no nuxi typecheck either). Local verification
is vitest plus nuxt generate plus a run against sdf-serve, the headless server from open PR
#2546. Every /api/* request through sdf-serve
carries the x-platform-token header; there is no tokenless read there. Styles use only --d-*
design tokens (the vocabulary is in packages/ts/ui/docs/design-system.md section 2, values in
packages/ts/ui/app/assets/css/tokens.css; decision
docs/planning/decisions/D-DLNG-design-language.md).
Commands, from solutions/ontological/desktop/src-nuxt:
bunx vitest run app/utils/entityRegistry.test.ts(and any new util test)bun run generate(runsnuxt generate)- Start
sdf-servewithmoon run sdf:serve(127.0.0.1:16175). Paste the token (the file path it prints at startup, orSDF_PLATFORM_TOKEN) in Settings. Open/model/entities/notes/N-YQVC?project=<id>and check AC-5 to AC-8 by eye.
Out of scope
Section titled “Out of scope”- Writing: no Edit mode, no checkbox toggling (reading mode listens for no
task-toggle). - A note tree view or graph;
parentis one link. - Capability, milestone, task and roadmap pages (other M-YX34 tasks).
- Bespoke note creation or the adoption wizard.
Dependencies
Section titled “Dependencies”T-1ODN: list rows withgenreandparent, and thenotesroute.- Open PR #2546 for
sdf-serve(live check only).
Discovery context
Section titled “Discovery context”Found while scoping M-YX34’s Notes deliverable.
Depends on
Section titled “Depends on”T-1ODN