Plota docs

12. Known limitations

Collected from the code, from its comments, and from driving the app. Nothing here is a plan; it is what the thing does not do today.

Rendering and export#

  • PDF artwork is a raster. Page geometry is vector-exact (media box in points, multi-page splitting, A4 tiling), but the artwork is a JPEG at 200 dpi by default, so PDF text is not selectable or searchable and deep zooms show pixels. This is deliberate: the PDF is rasterised from the exporter's own SVG so the two can never look different. dpi: 300 is the print setting.
  • SVG, PNG, draw.io and Mermaid are per page. Only Plota JSON and PDF cover a whole multi-page document in one file; exporting the others with scope All pages writes one file per page.
  • Mermaid export is a flowchart projection. Shape kinds collapse to the handful of Mermaid node shapes; styling, layers, data and pages are dropped.
  • A document theme change rewrites the theme's style keys. Per-node overrides survive, but there is no per-page theme.

Import#

  • A foreign SVG becomes one image node. Only SVGs this app wrote are re-read as a real document. The importer says which one you got.
  • Mermaid import covers flowchart / graph (with a direction) and sequenceDiagram. Anything else is refused with unsupported diagram type.
  • The PlantUML reader is a deliberately small subset: declarations (participant, actor, class, component, database, entity, usecase, …), the arrow spellings, [*] terminals and title. Skinparams, notes, packages and nested blocks are ignored.
  • Lucidchart import reads the clipboard's text/html flavour, not .lucid files. Unknown block classes fall back to a plain rectangle rather than dropping the paste.
  • draw.io shapes outside the mapped style vocabulary become rectangles. The warning list in the import dialog names what was approximated.

Routing#

  • Only orthogonal connectors with no hand-placed waypoints and both ends on a node take part in the whole-graph pass. Elbow, straight, curved, waypointed and free-ended connectors are routed individually, so they get no port distribution and no lane separation.
  • The lattice A* is O(cells²) in the number of obstacle borders. Obstacles are windowed to the corridor between the two ports (and the search is redone unwindowed if the windowed answer turns out dirty), but on very large pages routing is still the expensive part of a re-render. The code's own figure: about 20 ms an edge on a 200-shape document if it had to consider every shape.
  • Lane separation is best-effort. A shift that would push a route into a shape is rolled back, so overlapping parallel runs can remain where there is no room to separate them.

Text#

  • Auto-fit does not apply to containers and renderer-driven kinds: plane, group, fragment, lane, lifeline, activation and image are skipped and reported as skipped, because sizing them from their own label would collapse them over their children.
  • ERD attribute rows are drawn by the ERD stencil, not the shared label renderer. The shared renderer treats a newline as ordinary whitespace and re-wraps the body into a centred block, which cannot line an attribute up with its divider. Consequence: inline markup (**bold**, links, …) does not apply inside attribute lines — only in the entity name.
  • The same is true of the other content-driven stencils (table, umlClass): their body text is a model, not prose.

Chrome and extensibility#

  • registerOverlay({ layer: 'screen' }) is never rendered. Canvas.tsx draws listOverlays('canvas') only, so a screen-layer overlay registers successfully and then does nothing.
  • registerPanel({ side: 'bottom' }) is never rendered either: the Shell renders listPanels('left') and listPanels('right').
  • The right rail's active tab is component-local state, so no command can switch it to a particular tab. That is why the Help panel is registered on the left, where setLeftTab is a store action.
  • Two toolbar tooltips are wrong. The rulers button says ⇧R; the binding is Alt R. The Connector end: … palette commands report that "the canvas shows a plain arrow" — it does not, the canvas draws the UML and crow's-foot heads exactly as the exporters do.
  • F1 and ? are missing from the shortcut table. They are bound by the help feature, which reads ALL_SHORTCUTS but does not write to it, so the generated reference does not list the key that opens it.
  • displayCombo('mod++', true) renders as , dropping the +, so the macOS column for Zoom in reads oddly wherever the table is printed.

Persistence and AI#

  • No autosave, no server, no accounts, no collaboration. Ctrl/⌘ S writes to localStorage; clearing site data deletes it. Export JSON for anything that matters.
  • WebLLM needs WebGPU and a one-off download of roughly 1.5 GB. Without WebGPU the panel says so and downloads nothing.
  • Embeddings need the model CDN on first use (~23 MB, then cached). Offline the semantic features degrade to lexical similarity and say so.
  • Ollama cannot be reached from a file:// page — there is no origin to put in OLLAMA_ORIGINS. Serve the single-file build over http:// if you want a local model with it.
  • The single-file build stubs the three heavy in-browser models — WebLLM, transformers.js (embeddings) and background removal — because inlining them would add gigabytes. It does not stub the network providers: Ollama and any OpenAI-compatible server work normally, since they are plain fetch calls. Serve the file over http:// rather than opening it as file://, or the browser sends a null origin that Ollama's CORS check rejects.
  • A model can produce a valid but wrong plan. The op schema is enforced, so it cannot corrupt the document, but it can still propose nonsense — which is why nothing is applied without a preview.