Repository navigation
Fixing layout - #56
Merged
Merged
Conversation
Both diagrams are written out as images whenever the generated diagram changes, on by default. The ER diagram exports SVG and PNG through d2, whatever renderer is configured: the interactive renderer is a browser application with no headless path, and d2 is the only format that both renders images itself and keeps column-level edges. Its .d2 source is written alongside the diagram for the other renderers purely to be rendered. The architecture diagram exports PNG, and optionally JPG, through likec4 export. There is no SVG option because the CLI offers none - it renders by screenshotting each view in a headless browser. The export container reuses the server's single bind mount and working directory so the relative include paths and image aliases in likec4.config.json resolve to the same files. Export is gated on the same comparison that guards the file write, so an unchanged model exports nothing, and runs in the background so it never delays startup. Failures are best-effort: they are reported and leave the generated diagram untouched. Image export is disabled in TestAppHostFixture alongside FormatGeneratedFile, for the same reason plus one more - the fixture deletes its output directory on teardown, which would race an export still writing into it. ImageExportTests covers both exporters against the real containers instead.
Both diagrams now export into ./.aspirec4/images/ rather than each writing into its own generated-output directory. The ER diagram's images were landing among the interactive viewer's index.html, app.js and vendor/, which is the last place anyone would look. The architecture diagram's files are prefixed "architecture-", because likec4 export names each file after its view id, and a bare index.png says nothing in a shared folder - it could collide with the ER diagram outright. They are exported to a staging directory and moved in, since the CLI gives no say over the name. The ER diagram's images drop the ".gen" marker: it earns its place on a generated source file in a directory a consumer also hand-edits, but not on an image. The directory is resolved from the AppHost directory rather than either diagram's OutputDirectory, so the two cannot drift apart when only one is overridden. The ER diagram's dashboard row gained an Images property with the resolved path and an Open Diagram Images command, because the dashboard cannot navigate to a file:// path from its own origin. Both container invocations now bind their mounts with --mount instead of -v. A Windows host path already contains a colon, so "C:\...\erd:/data:ro" left the runtime guessing where the drive letter ended and the mode began - and it guessed wrong, failing with "invalid volume specification" under load. --mount parses key=value and cannot be ambiguous. ImageExportTests is marked [NotInParallel]: every test there starts a container, and several at once alongside the rest of the suite loaded the runtime enough to make it reject valid invocations. Its assertions now carry what the exporter logged, so a failed export explains itself instead of reporting a count of zero.
A property the model readers have no store type for was taken for a navigation, so it gained a table named after the framework type. A `JsonDocument Payload` field produced a table called "JsonDocument" that is in nobody's model, and because the metadata reader also walks a navigation's own properties, one unmapped type dragged a tree of them in behind it: a model with a JsonDocument and an XDocument field produced 11 tables instead of 1. Two fixes, applied to both the analyser and the reflection-based metadata reader, which had the same gap. The System.Text.Json document types now resolve to a `json` store type, so such a field appears as the column it is. And a type whose namespace is rooted in System or Microsoft is never treated as a navigation target, so the next unmapped framework type does not reintroduce the same class of bug. Asserted on the parsed tables rather than on the descriptor text, because the CLR type name legitimately appears there on the column itself.
The d2 export is a different diagram, not a differently-styled one: d2 draws sql_table shapes with its own theme and layout, while the viewer draws React Flow cards with context colours, key badges, complex-type groups and crow's-foot markers. WithViewerFidelityExport() renders through the viewer itself so the file matches its Save as SVG/PNG output exactly. Off by default: the viewer only runs in a browser, so it pulls a 3.7 GB image and takes seconds rather than milliseconds. Interactive only; every other renderer exports through d2 and says so. The viewer gained a __aspirec4RenderImage hook, split out of its download path, so a headless render can take the data URL directly instead of intercepting a browser download. The script drives chromium over raw CDP using only Node's own APIs, because the Playwright image ships the browsers but not the playwright package, and installing one would put a network fetch on a path meant to be hermetic. Note that toSvg returns percent-encoded text while toPng returns base64, so the decode keys off the declared encoding rather than the format. Export is now also gated on what is on disk. The in-memory change check starts empty every time the AppHost does, so a restart re-rendered images that were already correct - on the viewer path that meant pulling and starting a browser to reproduce identical bytes. A hash stamp beside the images records the model, the formats, the renderer and which renderer produced them, and is checked before anything is written or run. It is only trusted while the files it describes exist, so clearing the folder still re-exports, and only written once every requested format landed. Publish exports nothing at all. An export is fire-and-forget and publish exits immediately after generating its output, so it would be killed part-way through; publish is also expected to be hermetic rather than reaching for a container runtime a build agent may not have. The rule is asserted directly through ShouldExportImages, because driving it through a publish cannot tell "skipped" apart from "had not finished yet" - the first attempt at that test passed with the guard removed.
The images are the one piece of generated output worth keeping: a diagram that shows up in a pull request is documentation. The generated directory stays ignored, with the images carved out of it - note the trailing /* rather than /, because git will not descend into an excluded directory and the re-include would otherwise be unreachable. The stamp beside them is a local cache and stays ignored. The sample AppHost's own images are committed as the worked example. The stamp now ignores the port in an endpoint URL. The architecture model embeds the URLs Aspire allocated and those ports differ on every run, so hashing them verbatim invalidated the stamp every single time. Only the port is replaced, so a host or scheme that genuinely moved is still noticed. That does not make the architecture diagram fully cached, and the documentation no longer claims it does. Its run-state tags also settle during startup, and those genuinely change the picture, so normalising them away would hide a real difference. Measured across restarts the exported PNGs come out byte-for-byte identical, so git sees nothing - what is left is a background render rather than a dirty tree. The ER diagram, which is derived from the EF model alone, is fully cached and renders nothing at all on an unchanged schema.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.