Skip to content

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.ts already maps the affected screens to app/routes/home.tsx, app/routes/project.tsx, and app/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.data row. No directus/schema/*.json snapshot 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.tsx remains 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/ or app/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 window or browser storage.
  • pnpm verify typecheck passes.

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 retaining No description for 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 in app/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 typecheck passes.

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 typecheck passes.

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 typecheck and pnpm verify format from the repository root; use pnpm verify build if 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 typecheck passes.
  • pnpm verify format passes without unrelated file churn.
  • pnpm verify build passes 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.data payload and introduces no new persisted field.
  • None requiring a product decision before implementation.