# Racket Wiki CMap component This directory contains the complete browser-side CMap component. `cmap.js` is the ES-module entry point of the diagram engine. Its public `DiagramEngine`, `DiagramNode` and `DiagramLink` classes expose only the API used by the wiki editor: creating nodes and links, changing their presentation, connecting endpoints, zooming and receiving render, selection, activation and move events. The engine does not install a `window.Cmap` global. The modules under `engine/` separate that API from the renderer internals. `diagram-engine.js` owns the surface and component lifetime; `diagram-node.js`, `diagram-link.js` and `diagram-component.js` define the public handles. The `drawing-*` modules contain DOM rendering, geometry, relations, hit testing and pointer interaction. That rendering core is based on the MIT-licensed ionstage/cmap 0.1.3 source and is maintained as part of racket-wiki. Activation is recognized inside the same hit-test and drag lifecycle as selection, so it does not depend on a DOM `dblclick` event that may be suppressed by dragging. `diagram-group.js` adds the generic view-level `DiagramGroup` abstraction. A group contains nodes or nested groups, calculates a frame from their geometry, and can be expanded or collapsed. It deliberately knows nothing about CMaps, wiki pages or aspects. `DiagramEngine.setFilter` accepts an application policy for component visibility; the engine combines that policy with each component's own visibility and hides links whose endpoints are hidden. A wiki adapter can therefore translate submap membership or aspect matching into groups and filters without putting domain rules into the drawing engine. Proxy endpoints and boundary navigation remain application concerns until a generic endpoint projection API is introduced. `model/concept-repository.js` owns shared concepts, semantic concept relations and concept ownership. `model/concept-map.js` owns one map's concept placements, linking phrases, connectors, metadata and presentation values. `model/cmap-repository.js` owns the server boundary: it decodes stored CMaps, normalizes legacy concept identities and converts backend documents to and from `CmapModel`. It is the only browser-side CMap model module that knows the `/api/cmaps` routes. `model/appearance.js` owns the shared named styles, colour palette, validation and style matching. `model/appearance-repository.js` serializes that model through the appearance API. `model/settings-repository.js` owns the wiki start CMap and the authenticated user's A4-guide and per-CMap-context zoom settings. `model/people-repository.js` is the server boundary for the people referenced by concept tags. The repository objects keep only an in-memory session copy; the backend remains authoritative. None of these model modules contains DOM or drawing-engine objects, and CMap state is not persisted in browser storage. `cmap-view.js` owns the canvas and its concrete `DiagramEngine` instance. `view/appearance-editor.js` presents the appearance model in the concept dialog and coordinates explicit style and palette changes with its repository. The concrete dialog controllers live in `../wiki/cmap/dialogs/`. They own their fields, validation and browser events. The wiki-specific modules under `../wiki/cmap/` form the editor host layer. `CmapEditorHost` owns creation and destruction of the active editor; `CmapEditorUiController` owns the standard toolbar actions; `CmapStorageController` owns dirty state, autosave and save ordering; `CmapNavigationController` owns routes and browser context; `CmapTransferController` owns import and export; `CmapMapController` owns stored-map workflows; and `CmapConceptController` owns dialog-backed concept workflows. `CmapWorkspaceController` composes these modules, supplies the active CMap and registers the remaining dialog and DOM integration. Controllers use repository APIs for stored CMaps and people instead of calling backend routes directly. `cmap-racket-wiki.js` contains the wiki-specific editor controller, selection state, content-based initial sizing, resize and relation controls. Its view records connect pure model ids to drawing nodes without making those nodes part of the model. Automatic sizing remains active while text is edited and is disabled when the user resizes a concept manually. Selected concepts expose an edit handle. The host opens a modal editor for their shared name, aspects, people, description page, synopsis, image, external web address and linked wiki/CMap target, plus placement-specific background colour and typography. The editor has one compact tab for content, links and images, and a second tab for detailed colours, typography and submap presentation. Detailed settings can be saved as named, wiki-wide database styles; selected styles are applied immediately and non-default styles can be deleted. Its action bar remains visible while a tab scrolls. Structural kinds such as a sub-CMap head remain placement roles. Page concepts retain their linked `pageSlug`; double-click is reserved for opening that wiki page. Concepts may alternatively retain a `cmapSlug` for another stored CMap or a `parentCmapLink` for navigation back from a child map. An optional `externalUrl` accepts only HTTP(S), opens in a new browser tab and is shared by every placement of that concept. Images are data URLs inside the persisted CMap JSON document. Font families are selected from a practical list and font sizes are stored in typographic points. The host can change the linked page or CMap independently of the visible label and provides background and text-colour pickers. Linking phrases are compact borderless nodes at the intentional bend between two connector segments. Their position is stored with the rest of the map. When an endpoint is deleted, a genuinely branching phrase is retained while it still has at least one incoming and one outgoing concept. Losing the final endpoint on either side removes the phrase and all its remaining segments. Multiple selected items can be moved or deleted together. Grouping is a structural operation: the editor creates an expanded sub-CMap with a named main concept and adopts the selection as its contents. Ungrouping dissolves a selected inline sub-CMap or moves selected child items one level outward. **Make separate CMap** extracts the descendants into a complete stored CMap. The former sub-CMap head remains in the parent as an ordinary concept whose shared `cmapSlug` links to the new map. Ownership from that concept to the extracted concepts is removed; internal child connectors move with the child, while relations crossing the boundary terminate on the retained owner concept. The same action converts an older `derivedView` in place: its slug, title and metadata remain intact, but its document no longer depends on the parent CMap. `toDocument` and `loadDocument` round-trip the complete editor model: items, formatting, positions, connectors, recursive submap membership and promoted map references. The public `currentModel`, `loadModel` and `replaceModel` methods let the workspace pass complete domain models to and from the editor. The CMap repository, rather than the workspace, persists those models. The editor also provides `snapshotSvg()` for an embed render. That SVG is saved in the separate `cmap_renders` backend relation and is never inserted into the editable CMap document. `CmapEmbedView` renders this stored SVG as a linkable image for `{{cmap:...}}` Markdown embeds. ## JSON interchange `model/interchange.js` defines and validates the versioned `racket-wiki-cmap-bundle` format. `model/json-exporter.js` builds complete bundles through `CmapRepository` and the supplied page and attachment loaders. `model/json-importer.js` reads those bundles, stores CMaps through the same repository and performs the required page and attachment writes. The workspace decides only how conflicts are presented to the user. A bundle mirrors the normalized database model: `cmaps[]` contains the complete placement and presentation document, `concepts[]` contains shared content once per UUID, and `pages[]` contains the current Markdown, tags and referenced attachments of linked wiki and explanation pages. Attachments retain their MIME type and binary base64 content; import uploads them under the target page and rewrites the Markdown to the newly allocated URL. The root CMap and every included CMap retain their stable slug. Derived CMaps automatically include their source CMap. Presentation includes coordinates, dimensions, colours, font settings, sub-CMap membership, linking phrases and connectors. Connector endpoints use local item ids; concept identity never depends on those local ids. The formal contract is [`racket-wiki-cmap-bundle-v1.schema.json`](../schemas/racket-wiki-cmap-bundle-v1.schema.json). An external generator may use a temporary id such as `new:security-review` for a new concept. Every concept must also occur in at least one non-phrase `document.items[]` placement with coordinates. On import the server turns such a temporary identity into a UUID through the central Racket UUID helper. UUIDs already present in an export remain unchanged. Imported CMap slugs likewise remain unchanged. `model/markdown-exporter.js` reads stored models through `CmapRepository` but produces a readable report instead of an importable bundle. It is independent of the JSON exporter because the report follows linked maps and optional wiki pages for a different purpose. Additional output formats can therefore be implemented as separate exporters without adding format-specific code to the workspace. The editor keeps up to one hundred complete document states for Undo and Redo. One drag or resize gesture forms one history step. The public `undo`, `redo`, `canUndo`, `canRedo` and `resetHistory` methods are also used by the wiki host for keyboard shortcuts and context-menu state. `cmap.css` contains all CMap presentation and interaction styles. The source files deliberately remain separate but form one component and are loaded directly from `/js/cmap/`; there is no runtime download or vendor patch.