Markdown Descriptions — Implementation Plan¶
Decisions¶
| Decision | Choice | Why |
|---|---|---|
| Description scope | User-authored project, map, and map-object descriptions | Imported AvesMaps settlement descriptions are explicitly out of scope. |
| Authoring surface | Keep the existing textareas and store the submitted Markdown string unchanged | A toolbar, rich-text editor, and form preview are deferred. |
| Supported syntax | Bold, italic, strikethrough, ordered lists, unordered lists, and headings h1-h6 | These are the refined task requirements. |
| Deferred syntax | Links and underline | They were explicitly deferred and must not be implied as supported. |
| Parser strategy | Add no package dependency; implement a deliberately limited local parser/renderer for the supported syntax | The repository has no Markdown dependency and the requested syntax can be rendered as React elements without injecting HTML. |
| Safety model | Render parsed text and elements directly; do not use dangerouslySetInnerHTML or execute arbitrary HTML |
User-authored descriptions must not create executable or unsafe markup. |
| Display modes | Full rendering in detail panels; compact/truncated rendering in project and map list rows | Full block Markdown can make compact navigation rows excessively tall. |
| Persistence | No data-model or Directus migration | Descriptions already persist as strings inside the existing map_projects.data payload. |
| Verification | pnpm verify typecheck and pnpm verify format, plus a manual rendering/security matrix |
The repository has no configured test suite and the task is primarily visual/rendering behavior. |
Prerequisites¶
- The protected route table in
app/routes.tsalready maps the affected screens toapp/routes/home.tsx,app/routes/project.tsx, andapp/routes/project-map.tsx; no new route is required. - The existing description fields are optional strings on
MapProject,MapRef, and the shared map-object shape. Preserve those public shapes and continue submitting raw textarea values. - Project persistence uses the existing local JSON or Directus
map_projects.datarow. Nodirectus/schema/*.jsonsnapshot exists, so no new Directus field may be planned or assumed. - Do not render imported AvesMaps descriptions through the new component in this task;
app/features/avesmaps/avesmaps-feature-detail-panel.tsxremains plain text. - The protected routes already run
requireSessionMiddleware; no new loader/action authorization path is needed because this task does not add a server mutation. - Keep the implementation SSR-compatible and browser-independent. The renderer must be usable from route components without Leaflet or client-only imports.
Step 1 — Define the safe Markdown renderer contract¶
Goal: Create one reusable, dependency-free rendering boundary for the supported Markdown subset.
Scope
- In: Add a shared renderer under
app/components/orapp/lib/with a typed component/function accepting an optional description string and a display mode for full versus compact output. - In: Define parsing behavior for paragraphs, inline bold, italic, strikethrough, ordered lists, unordered lists, and headings h1-h6; preserve ordinary text and multiline plain-text descriptions.
- In: Render React elements and text nodes directly, with explicit handling for unmatched or unsupported syntax so it remains harmless/readable.
- In: Establish stable semantic element structure and class hooks for spacing, typography, list indentation, heading scale, and compact truncation.
- Out: Links, underline syntax, arbitrary raw HTML, code blocks, images, tables, nested advanced Markdown extensions, and changes to stored data.
Acceptance criteria
- The renderer has a small, documented supported-syntax contract that matches the refined task.
- Plain text, empty strings, multiline text, and unsupported syntax remain readable.
- Bold, italic, strikethrough, ordered lists, unordered lists, and h1-h6 produce semantic React elements rather than literal formatting markers.
- No
dangerouslySetInnerHTML, raw HTML execution, or unsafe URL handling is introduced. - Full and compact modes have stable output classes and do not shift surrounding layout unexpectedly.
- The renderer can be imported by SSR route components without accessing
windowor browser storage. -
pnpm verify typecheckpasses.
Step 2 — Apply Markdown rendering to project and map displays¶
Goal: Replace raw user-authored project and map description output while preserving current fallbacks and editing behavior.
Scope
- In:
app/routes/home.tsx, render project descriptions with the compact renderer while retainingNo descriptionfor missing values. - In:
app/routes/project.tsx, render map-list descriptions compactly, retain the map-source fallback, and render the project description in the project-level display/details area. - In:
app/routes/create.tsx,app/routes/add-map.tsx, and the project settings form inapp/routes/project.tsx, keep the existing textareas and ensure their values are submitted unchanged. - In: Reuse the same renderer and mode contract rather than adding route-specific Markdown parsing.
- Out: Changes to project/map actions, persistence APIs, route declarations, imported AvesMaps content, or link/underline support.
Acceptance criteria
- Existing create/edit textareas continue to accept multiline Markdown without conversion.
- Project Markdown renders on the home list and project-level detail display.
- Map Markdown renders in the project map list and map detail display where the map description is shown.
- Missing project descriptions still show
No description; missing map descriptions still show the source label. - Compact list rows remain bounded and readable when descriptions contain headings or lists.
- Existing plain-text project and map descriptions remain readable.
-
pnpm verify typecheckpasses.
Step 3 — Apply Markdown rendering to map-object detail panels¶
Goal: Render descriptions consistently for points, lines, and areas in the shared object detail surface.
Scope
- In:
app/features/map-objects/map-object-detail-panel.tsx, replace the raw paragraph with the full Markdown renderer when an object description exists. - In:
app/features/map-objects/map-object-form.tsx, preserve the current description textarea and default value behavior for all three object kinds. - In:
app/routes/project-map.tsx, keep the existing add/update action payloads as raw description strings and continue passing the shared object shape to the detail panel. - In:
app/lib/projects.server.ts, make no persistence-format change; verify existing add/update functions continue storing optional strings unchanged. - Out: Changes to object labels, geometry, edit forms beyond Markdown entry, or imported AvesMaps detail content.
Acceptance criteria
- Point, line, and area descriptions use the same full renderer.
- Empty or missing object descriptions omit the description content as they do today.
- Existing object descriptions remain loadable and editable without migration.
- Markdown content cannot execute markup or scripts in the object detail panel.
- The existing protected map route and access-token flow remain unchanged.
-
pnpm verify typecheckpasses.
Step 4 — Verify formatting, compatibility, and responsive presentation¶
Goal: Confirm the complete description workflow works across persisted data, SSR output, compact rows, and detail panels.
Scope
- In: Run
pnpm verify typecheckandpnpm verify formatfrom the repository root; usepnpm verify buildif the renderer introduces SSR or bundling issues that typecheck cannot detect. - In: Perform a manual matrix covering project creation/editing, map creation/editing, point/line/area creation/editing, home/project/map/object displays, missing descriptions, existing plain text, supported formatting, unsupported links/underline, and unsafe HTML/script input.
- In: Check desktop and narrow viewport layouts for compact list rows and the map-object detail panel; confirm headings/lists do not overlap controls or escape their containers.
- In: Confirm local JSON and Directus-backed project data still load without a migration and that imported AvesMaps descriptions remain unchanged.
- Out: Adding a test framework, migrating existing records, or implementing deferred link/underline/imported-data support.
Acceptance criteria
-
pnpm verify typecheckpasses. -
pnpm verify formatpasses without unrelated file churn. -
pnpm verify buildpasses when run as the full SSR/bundling check. - Manual checks confirm supported formatting in every in-scope display surface.
- Manual checks confirm missing descriptions and existing plain text retain current behavior.
- Manual checks confirm links and underline are not treated as supported features.
- Manual checks confirm unsafe HTML/script input is displayed harmlessly and never executed.
- Manual checks confirm compact rows and detail panels remain readable on desktop and narrow viewports.
Risks / Open Questions¶
- A local parser without a maintained Markdown dependency has a deliberately limited syntax surface; implementation must keep unsupported syntax harmless and document the boundary rather than implying CommonMark compatibility.
- Compact rendering may omit or truncate block-level content in list rows. The full description remains available in detail views, and the truncation behavior must be visually verified.
- No local Directus schema snapshot exists. This plan intentionally relies on the existing nested
map_projects.datapayload and introduces no new persisted field. - None requiring a product decision before implementation.