@nimtech/nimpress 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/.claude/rules/changelog-entries.md +229 -0
  2. package/.claude/rules/chunk-cycles.md +23 -0
  3. package/.claude/rules/component-modules.md +152 -0
  4. package/.claude/rules/dbml-pages.md +118 -0
  5. package/.claude/rules/deploy.md +47 -0
  6. package/.claude/rules/doc-pages.md +93 -0
  7. package/.claude/rules/docs-authoring.md +90 -0
  8. package/.claude/rules/docs-sync.md +59 -0
  9. package/.claude/rules/file-layout.md +49 -0
  10. package/.claude/rules/frontmatter.md +91 -0
  11. package/.claude/rules/page-types.md +108 -0
  12. package/.claude/rules/paths.md +27 -0
  13. package/.claude/rules/roadmap-entries.md +154 -0
  14. package/.claude/rules/style.md +61 -0
  15. package/LICENSE +201 -0
  16. package/README.md +137 -0
  17. package/bin/nimpress.mjs +7 -0
  18. package/config.schema.json +555 -0
  19. package/dist/DbmlNote-CTwTd5vM.js +21 -0
  20. package/dist/DbmlTable-BXIyJnLV.js +209 -0
  21. package/dist/cli.es.js +1265 -0
  22. package/dist/nimpress.es.js +8896 -0
  23. package/dist/plugin-DQtunTIv.js +8268 -0
  24. package/dist/plugin.es.js +23 -0
  25. package/dist/src/api/CodeExamples.svelte.d.ts +1 -0
  26. package/dist/src/api/MethodBadge.svelte.d.ts +1 -0
  27. package/dist/src/api/OpenApiRoot.svelte.d.ts +1 -0
  28. package/dist/src/api/Operation.svelte.d.ts +1 -0
  29. package/dist/src/api/ParamRow.svelte.d.ts +1 -0
  30. package/dist/src/api/Schema.svelte.d.ts +1 -0
  31. package/dist/src/api/TryDialog.svelte.d.ts +1 -0
  32. package/dist/src/api/TryPanel.svelte.d.ts +1 -0
  33. package/dist/src/api/refs.d.ts +12 -0
  34. package/dist/src/api/refs.d.ts.map +1 -0
  35. package/dist/src/api/tryState.d.ts +21 -0
  36. package/dist/src/api/tryState.d.ts.map +1 -0
  37. package/dist/src/api/types.d.ts +63 -0
  38. package/dist/src/api/types.d.ts.map +1 -0
  39. package/dist/src/auth/AccountMenu.svelte.d.ts +1 -0
  40. package/dist/src/auth/guard.d.ts +5 -0
  41. package/dist/src/auth/guard.d.ts.map +1 -0
  42. package/dist/src/auth/session.d.ts +8 -0
  43. package/dist/src/auth/session.d.ts.map +1 -0
  44. package/dist/src/banner.d.ts +23 -0
  45. package/dist/src/banner.d.ts.map +1 -0
  46. package/dist/src/cli/export.d.ts +4 -0
  47. package/dist/src/cli/export.d.ts.map +1 -0
  48. package/dist/src/cli/guard.d.ts +3 -0
  49. package/dist/src/cli/guard.d.ts.map +1 -0
  50. package/dist/src/cli/init.d.ts +2 -0
  51. package/dist/src/cli/init.d.ts.map +1 -0
  52. package/dist/src/cli/lint.d.ts +11 -0
  53. package/dist/src/cli/lint.d.ts.map +1 -0
  54. package/dist/src/cli/modules.d.ts +3 -0
  55. package/dist/src/cli/modules.d.ts.map +1 -0
  56. package/dist/src/cli/shared.d.ts +6 -0
  57. package/dist/src/cli/shared.d.ts.map +1 -0
  58. package/dist/src/cli/site.d.ts +7 -0
  59. package/dist/src/cli/site.d.ts.map +1 -0
  60. package/dist/src/cli.d.ts +5 -0
  61. package/dist/src/cli.d.ts.map +1 -0
  62. package/dist/src/config/base.d.ts +4 -0
  63. package/dist/src/config/base.d.ts.map +1 -0
  64. package/dist/src/config/chunkCycles.d.ts +10 -0
  65. package/dist/src/config/chunkCycles.d.ts.map +1 -0
  66. package/dist/src/config/defaults.d.ts +4 -0
  67. package/dist/src/config/defaults.d.ts.map +1 -0
  68. package/dist/src/config/html.d.ts +3 -0
  69. package/dist/src/config/html.d.ts.map +1 -0
  70. package/dist/src/config/load.d.ts +20 -0
  71. package/dist/src/config/load.d.ts.map +1 -0
  72. package/dist/src/config/paths.d.ts +8 -0
  73. package/dist/src/config/paths.d.ts.map +1 -0
  74. package/dist/src/config/schema.d.ts +1238 -0
  75. package/dist/src/config/schema.d.ts.map +1 -0
  76. package/dist/src/config/viteConfig.d.ts +11 -0
  77. package/dist/src/config/viteConfig.d.ts.map +1 -0
  78. package/dist/src/dbml/erd.d.ts +59 -0
  79. package/dist/src/dbml/erd.d.ts.map +1 -0
  80. package/dist/src/framework/configStore.d.ts +7 -0
  81. package/dist/src/framework/configStore.d.ts.map +1 -0
  82. package/dist/src/framework/createNimpressApp.d.ts +10 -0
  83. package/dist/src/framework/createNimpressApp.d.ts.map +1 -0
  84. package/dist/src/framework/gated.d.ts +5 -0
  85. package/dist/src/framework/gated.d.ts.map +1 -0
  86. package/dist/src/framework/hashSpy.d.ts +7 -0
  87. package/dist/src/framework/hashSpy.d.ts.map +1 -0
  88. package/dist/src/framework/pageMeta.d.ts +4 -0
  89. package/dist/src/framework/pageMeta.d.ts.map +1 -0
  90. package/dist/src/framework/pageStyles.d.ts +2 -0
  91. package/dist/src/framework/pageStyles.d.ts.map +1 -0
  92. package/dist/src/framework/router.d.ts +5 -0
  93. package/dist/src/framework/router.d.ts.map +1 -0
  94. package/dist/src/framework/stores/feed.d.ts +8 -0
  95. package/dist/src/framework/stores/feed.d.ts.map +1 -0
  96. package/dist/src/framework/stores/sidebar.d.ts +4 -0
  97. package/dist/src/framework/stores/sidebar.d.ts.map +1 -0
  98. package/dist/src/framework/stores/theme.d.ts +6 -0
  99. package/dist/src/framework/stores/theme.d.ts.map +1 -0
  100. package/dist/src/framework/stores/viewer.d.ts +6 -0
  101. package/dist/src/framework/stores/viewer.d.ts.map +1 -0
  102. package/dist/src/icons/FullscreenIcon.svelte.d.ts +1 -0
  103. package/dist/src/icons/IconAdd.svelte.d.ts +1 -0
  104. package/dist/src/icons/IconChevron.svelte.d.ts +1 -0
  105. package/dist/src/icons/IconClear.svelte.d.ts +1 -0
  106. package/dist/src/icons/IconConsole.svelte.d.ts +1 -0
  107. package/dist/src/icons/IconDock.svelte.d.ts +1 -0
  108. package/dist/src/icons/IconDownload.svelte.d.ts +1 -0
  109. package/dist/src/icons/IconJson.svelte.d.ts +1 -0
  110. package/dist/src/icons/IconMinus.svelte.d.ts +1 -0
  111. package/dist/src/icons/IconMock.svelte.d.ts +1 -0
  112. package/dist/src/icons/IconMoon.svelte.d.ts +1 -0
  113. package/dist/src/icons/IconOpen.svelte.d.ts +1 -0
  114. package/dist/src/icons/IconReload.svelte.d.ts +1 -0
  115. package/dist/src/icons/IconRemove.svelte.d.ts +1 -0
  116. package/dist/src/icons/IconShadow.svelte.d.ts +1 -0
  117. package/dist/src/icons/IconSun.svelte.d.ts +1 -0
  118. package/dist/src/index.d.ts +44 -0
  119. package/dist/src/index.d.ts.map +1 -0
  120. package/dist/src/layout/App.svelte.d.ts +1 -0
  121. package/dist/src/layout/AppRoot.svelte.d.ts +1 -0
  122. package/dist/src/layout/BackToTop.svelte.d.ts +1 -0
  123. package/dist/src/layout/Breadcrumbs.svelte.d.ts +1 -0
  124. package/dist/src/layout/GatedPage.svelte.d.ts +1 -0
  125. package/dist/src/layout/Header.svelte.d.ts +1 -0
  126. package/dist/src/layout/HomePage.svelte.d.ts +1 -0
  127. package/dist/src/layout/RightToc.svelte.d.ts +1 -0
  128. package/dist/src/layout/Sidebar.svelte.d.ts +1 -0
  129. package/dist/src/layout/SidebarNode.svelte.d.ts +1 -0
  130. package/dist/src/markdown/Actions.svelte.d.ts +1 -0
  131. package/dist/src/markdown/CalloutCheck.svelte.d.ts +1 -0
  132. package/dist/src/markdown/CalloutInfo.svelte.d.ts +1 -0
  133. package/dist/src/markdown/CalloutNote.svelte.d.ts +1 -0
  134. package/dist/src/markdown/CalloutTip.svelte.d.ts +1 -0
  135. package/dist/src/markdown/CalloutWarning.svelte.d.ts +1 -0
  136. package/dist/src/markdown/Card.svelte.d.ts +1 -0
  137. package/dist/src/markdown/CardGroup.svelte.d.ts +1 -0
  138. package/dist/src/markdown/ChangelogPage.svelte.d.ts +1 -0
  139. package/dist/src/markdown/CodeBlock.svelte.d.ts +1 -0
  140. package/dist/src/markdown/CodeEditor.svelte.d.ts +1 -0
  141. package/dist/src/markdown/CodeGroup.svelte.d.ts +1 -0
  142. package/dist/src/markdown/ComponentEmbed.svelte.d.ts +1 -0
  143. package/dist/src/markdown/ComponentPage.svelte.d.ts +1 -0
  144. package/dist/src/markdown/ControlNode.svelte.d.ts +1 -0
  145. package/dist/src/markdown/DBMLBlock.svelte.d.ts +1 -0
  146. package/dist/src/markdown/DbmlNote.svelte.d.ts +1 -0
  147. package/dist/src/markdown/DbmlPage.svelte.d.ts +1 -0
  148. package/dist/src/markdown/DbmlTable.svelte.d.ts +1 -0
  149. package/dist/src/markdown/DragDivider.svelte.d.ts +1 -0
  150. package/dist/src/markdown/Feature.svelte.d.ts +1 -0
  151. package/dist/src/markdown/FeatureGrid.svelte.d.ts +1 -0
  152. package/dist/src/markdown/HeroPage.svelte.d.ts +1 -0
  153. package/dist/src/markdown/MermaidBlock.svelte.d.ts +1 -0
  154. package/dist/src/markdown/Page.svelte.d.ts +1 -0
  155. package/dist/src/markdown/PlanetFooter.svelte.d.ts +1 -0
  156. package/dist/src/markdown/RoadmapBlob.svelte.d.ts +1 -0
  157. package/dist/src/markdown/RoadmapNode.svelte.d.ts +1 -0
  158. package/dist/src/markdown/RoadmapPage.svelte.d.ts +1 -0
  159. package/dist/src/markdown/SubscribeDialog.svelte.d.ts +1 -0
  160. package/dist/src/mock/index.d.ts +42 -0
  161. package/dist/src/mock/index.d.ts.map +1 -0
  162. package/dist/src/modules/autoStory.d.ts +5 -0
  163. package/dist/src/modules/autoStory.d.ts.map +1 -0
  164. package/dist/src/modules/componentData.d.ts +14 -0
  165. package/dist/src/modules/componentData.d.ts.map +1 -0
  166. package/dist/src/modules/harness.d.ts +25 -0
  167. package/dist/src/modules/harness.d.ts.map +1 -0
  168. package/dist/src/modules/importStorybook.d.ts +12 -0
  169. package/dist/src/modules/importStorybook.d.ts.map +1 -0
  170. package/dist/src/modules/lint.d.ts +3 -0
  171. package/dist/src/modules/lint.d.ts.map +1 -0
  172. package/dist/src/modules/pages.d.ts +10 -0
  173. package/dist/src/modules/pages.d.ts.map +1 -0
  174. package/dist/src/modules/parse/dts.d.ts +4 -0
  175. package/dist/src/modules/parse/dts.d.ts.map +1 -0
  176. package/dist/src/modules/parse/svelte.d.ts +3 -0
  177. package/dist/src/modules/parse/svelte.d.ts.map +1 -0
  178. package/dist/src/modules/parse/typeMembers.d.ts +64 -0
  179. package/dist/src/modules/parse/typeMembers.d.ts.map +1 -0
  180. package/dist/src/modules/parse/vue.d.ts +3 -0
  181. package/dist/src/modules/parse/vue.d.ts.map +1 -0
  182. package/dist/src/modules/resolve.d.ts +9 -0
  183. package/dist/src/modules/resolve.d.ts.map +1 -0
  184. package/dist/src/modules/schema.d.ts +53 -0
  185. package/dist/src/modules/schema.d.ts.map +1 -0
  186. package/dist/src/modules/stories.d.ts +4 -0
  187. package/dist/src/modules/stories.d.ts.map +1 -0
  188. package/dist/src/search/SearchModal.svelte.d.ts +1 -0
  189. package/dist/src/search/indexer.d.ts +5 -0
  190. package/dist/src/search/indexer.d.ts.map +1 -0
  191. package/dist/src/story/index.d.ts +17 -0
  192. package/dist/src/story/index.d.ts.map +1 -0
  193. package/dist/src/subscribe/subscribe.d.ts +5 -0
  194. package/dist/src/subscribe/subscribe.d.ts.map +1 -0
  195. package/dist/src/types.d.ts +531 -0
  196. package/dist/src/types.d.ts.map +1 -0
  197. package/dist/story.es.js +10 -0
  198. package/dist/style.css +1 -0
  199. package/dist/tailwind.preset.d.ts +4 -0
  200. package/dist/tailwind.preset.d.ts.map +1 -0
  201. package/dist/tailwind.preset.es.js +67 -0
  202. package/dist/typeMembers-CNvE92EX.js +538 -0
  203. package/package.json +125 -0
  204. package/src/harness/svelte/ComponentHarness.svelte +12 -0
  205. package/src/harness/svelte/ComponentHarnessEffects.svelte +24 -0
  206. package/src/harness/svelte/ComponentHarnessOverlay.svelte +9 -0
  207. package/src/harness/svelte/ComponentStory.svelte +9 -0
  208. package/src/harness/svelte/DefaultHarness.svelte +15 -0
  209. package/src/harness/svelte/HarnessRoot.svelte +22 -0
  210. package/src/harness/svelte/context.svelte.ts +33 -0
  211. package/src/harness/svelte/index.ts +12 -0
  212. package/src/harness/vue/ComponentHarness.vue +16 -0
  213. package/src/harness/vue/ComponentHarnessEffects.vue +27 -0
  214. package/src/harness/vue/ComponentHarnessOverlay.vue +12 -0
  215. package/src/harness/vue/ComponentStory.vue +16 -0
  216. package/src/harness/vue/DefaultHarness.vue +14 -0
  217. package/src/harness/vue/context.ts +37 -0
  218. package/src/harness/vue/index.ts +11 -0
  219. package/src/mock/index.ts +88 -0
  220. package/src/styles/app.css +3 -0
  221. package/src/styles/preflight.css +540 -0
  222. package/src/styles/theme.css +81 -0
  223. package/src/styles/tokens.css +150 -0
@@ -0,0 +1,229 @@
1
+ # Authoring changelog entries
2
+
3
+ Rules for writing release notes when the site uses `type: changelog`.
4
+
5
+ ## One file per release
6
+
7
+ Every release is its own markdown file. Reviewing a release is a single file PR. Combining releases in one file is a rule violation.
8
+
9
+ ## How files collapse into one page
10
+
11
+ Every entry file declares `title: Changelog` (or whatever the collection's display name is) and `type: changelog`. Files sit in one folder. The plugin groups by `(parent folder, title)` and produces one collection page mounted at that folder's path. Each file is one entry on the page.
12
+
13
+ The filename is arbitrary. Use the version (`v1.4.2.md`) so the file tree reads as a release log.
14
+
15
+ ## Required frontmatter
16
+
17
+ ```yaml
18
+ ---
19
+ title: Changelog
20
+ type: changelog
21
+ data:
22
+ version: 1.4.2
23
+ release_date: 2026-06-08
24
+ title: Booking calendars
25
+ description: Booking calendars become a first class resource.
26
+ ---
27
+ ```
28
+
29
+ 1. Top level `title` is the page title and the grouping key. Every entry file in the same folder uses the exact same string.
30
+
31
+ 2. `type: changelog` is required for the plugin to group the file.
32
+
33
+ 3. `data.version` is required. Newest version sorts first. Parsed as dot delimited numeric segments. Optional leading `v` is allowed.
34
+
35
+ 4. `data.release_date` is required. An RFC 3339 timestamp. Date only (`2026-06-08`) is accepted and interpreted as midnight UTC. Full timestamps (`2026-06-08T09:00:00Z`) are accepted. The renderer formats it as `DD.MM.YYYY` next to the entry title. Invalid values fail the build.
36
+
37
+ 5. `data.title` is the per release name. Hard limits:
38
+
39
+ 5.1. **Max 48 characters.** Builds fail above the limit.
40
+
41
+ 5.2. **No commas.** A comma means the title is trying to list more than one shipped thing — split it into two releases or move the rest to the body.
42
+
43
+ 5.3. **No code identifiers.** No backticks, no method names, no path segments, no query parameters, no field names, no SQL identifiers, no Go symbols, no `:`-prefixed param placeholders. If the only way to name the release is with an identifier, the title is wrong — pick the user-visible capability instead.
44
+
45
+ 5.4. **Sentence case, one phrase.** Examples to write: `Booking calendars`, `Organization settings`, `Conflict clarity`. Examples to reject: `Setting joins claimius, with-set hydration on the audit and relation endpoints, and an actor naming sweep`, `` `with=access` everywhere ``, `POST /api/calendar shipped`.
46
+
47
+ 6. `data.description` is one short sentence that frames the release for a reader scanning the changelog. Hard limits:
48
+
49
+ 6.1. **Max 160 characters.** Builds fail above the limit.
50
+
51
+ 6.2. **One sentence.** Period only at the end.
52
+
53
+ 6.3. **No code identifiers.** No backticks, no method names, no paths, no query parameters, no function names, no struct names, no SQL identifiers. The description is the introduction, not the implementation summary.
54
+
55
+ 6.4. **No detail enumeration.** Skip `routes through`, `hydrates`, `is renamed to`, multi-clause `and …, and …` constructions. Frame the why or the user-facing effect.
56
+
57
+ 6.5. Plain text only, markdown is not rendered.
58
+
59
+ ## Linking to a roadmap issue
60
+
61
+ `data.issue` on a changelog entry declares an explicit link to a roadmap issue file by relative path. The plugin resolves the path against the changelog file. `data.status` reports the effect of this release on the issue: a number `0..100` for partial progress, or the literal word `completes` (also `completed`, `complete`, `closes`, `fixes`, `resolves`) to flip the issue to `shipped`.
62
+
63
+ ```yaml
64
+ data:
65
+ version: 1.5.0
66
+ release_date: 2026-06-10
67
+ title: Organization settings
68
+ description: Plain text sentence.
69
+ issue: ../roadmap/organization-settings
70
+ status: completes
71
+ ```
72
+
73
+ 1. Use the issue path with or without the trailing `.md`.
74
+
75
+ 2. `data.status` as a number sets the rocket position; the highest number across all linked releases for the same issue wins.
76
+
77
+ 3. `data.status: completes` flips the issue to `shipped` and parks the rocket at this point.
78
+
79
+ 4. The release drops a marker on the roadmap spine at the changelog's `release_date`. Clicking the marker navigates back to the changelog entry.
80
+
81
+ ## Never set these on a changelog entry
82
+
83
+ 1. `path` is rejected. The route is derived from the entry folder.
84
+
85
+ 2. Top level `description` is ignored. Put it under `data` instead.
86
+
87
+ ## Layout in the file tree
88
+
89
+ Place every changelog file inside one directory:
90
+
91
+ ```
92
+ docs/
93
+ └── solutions/
94
+ └── bookable/
95
+ └── changelog/
96
+ ├── v1.4.0.md
97
+ ├── v1.4.1.md
98
+ └── v1.4.2.md
99
+ ```
100
+
101
+ The collection page lives at `/solutions/bookable/changelog`. Each file shares `title: Changelog` so they merge.
102
+
103
+ ## Version sort
104
+
105
+ Newest first. Parsed as dot delimited numeric segments. `1.10.0` ranks above `1.2.9`. Pre release suffixes are not parsed, they sort last among same base versions.
106
+
107
+ ## Entry body
108
+
109
+ The body carries the real detail of the release. The renderer applies its own scaled down styling for headings inside an entry, so H1 through H6 are all allowed and read as compact section markers, not page chrome.
110
+
111
+ 1. Lead with one short paragraph that names the shipped capability.
112
+
113
+ 2. Group user visible changes under headings: themes, not severity. One section per shipped capability. Use the heading level that reflects nesting depth.
114
+
115
+ 3. Heading text is a short friendly readable label. Never an identifier, method, path, query parameter, or backticked token. Examples to write: `Creator hydration`, `Booking calendars`, `Conflict clarity`. Examples to avoid: `` `with=creator` on every endpoint ``, `POST /api/calendar`, `inherit on POST endpoints removed`.
116
+
117
+ 4. Under each heading, write one prose paragraph that frames the change in human terms. Move every identifier, route, query parameter, status code, schema name, and token into that prose (or the code blocks beneath it), inside backticks. Identifiers belong in the body, not in the heading.
118
+
119
+ 5. Inside each section, use a bulleted or short prose list. One change per item, one sentence per item, with identifiers wrapped in backticks.
120
+
121
+ 6. Use fenced code blocks for HTTP exchanges, YAML, JSON, and request or response bodies. Inline `code` spans carry the identifiers inside prose.
122
+
123
+ 7. Link to the relevant PR or issue when the change references work outside the docs.
124
+
125
+ ## Markdown shapes that are encouraged
126
+
127
+ The body is rendered as full GitHub flavored markdown. Reach for whichever shape makes the change scan fastest, not just prose paragraphs.
128
+
129
+ 1. Bulleted lists for two or more sibling changes under one theme.
130
+
131
+ 2. Tables when the section is a list of `(thing, effect)` pairs, `(field, default value)` pairs, `(endpoint, status)` pairs, or any other rows that share the same columns. A table reads faster than five bullets that repeat the same sentence shape. Wrap identifiers in backticks inside cells.
132
+
133
+ 3. Fenced code blocks for HTTP exchanges, request bodies, response bodies, YAML, JSON, and SQL snippets. Tag the language so the docs panel highlights.
134
+
135
+ 4. Block quotes for a one line callout when a section needs to interrupt the flow with a warning or a note (use sparingly).
136
+
137
+ 5. Nested lists when a single change has two or three steps that the consumer runs in order; a flat bulleted list is the default and the nested shape is the exception.
138
+
139
+ A section that compares before and after, names defaults per field, or lists endpoints with their new status code is almost always clearer as a table than as a stack of bullets.
140
+
141
+ ## Linking from a changelog entry
142
+
143
+ A release that introduces a new example, a new concept page, or a new API endpoint links to the canonical doc page from inside the entry body. The reader scanning the changelog jumps straight to the deeper doc.
144
+
145
+ 1. New example walkthrough — link to `/solutions/<product>/examples/<slug>` from inside the prose of the section that ships the example.
146
+
147
+ 2. New concept page — link to `/solutions/<product>/concepts/<slug>` from the section that explains the new concept.
148
+
149
+ 3. New API endpoint — link to the docs panel anchor for that endpoint when one exists; otherwise link to the section in the relevant concept page that names the endpoint.
150
+
151
+ 4. Roadmap issue — set `data.issue` on the frontmatter (see above). Do not also paste the link in the body.
152
+
153
+ 5. Use markdown link syntax `[label](path)`. Relative paths are resolved against the changelog file; site root paths starting at `/solutions/` also work and read more obviously.
154
+
155
+ 6. Link text is the human label of the target, not the path. `the Admin booking management example` reads; `/solutions/bookable/examples/admin_booking_management` does not.
156
+
157
+ 7. Link once per target inside a single entry. A second mention in the same entry stays as plain text.
158
+
159
+ ## What to include
160
+
161
+ 1. User visible behavior changes.
162
+
163
+ 2. Breaking changes, called out explicitly. Bold the word `breaking` so the reader cannot miss it.
164
+
165
+ 3. New configuration fields, with the value to set.
166
+
167
+ 4. Removed features, with the migration path.
168
+
169
+ ## What not to include
170
+
171
+ 1. Internal refactors with no user visible effect.
172
+
173
+ 2. Dependency bumps that do not change behavior.
174
+
175
+ 3. Documentation only changes.
176
+
177
+ 4. Anything already obvious from the version number.
178
+
179
+ 5. Repository file paths, directory layouts, code-base file names, or any reference to where code lives in the source tree. The changelog is read by API consumers, not by repo contributors. Anything that names `internal/`, `cmd/`, `openapi_definition/`, `<op_id>_*.yml`, or a path under the repo is a rule violation. The API path (`/api/...`) is fine; the file path is not.
180
+
181
+ 6. Wording that names the same fact from two angles ("absent from the running surface and absent from the spec", "removed from the code and the spec", "dropped from both X and Y"). State the user-facing fact once. The reader does not see the spec and the running handlers as two separate things — only the API.
182
+
183
+ 7. Restating what the version number already conveys. A line that says "this release ships v1.5.1" carries no information.
184
+
185
+ ## How to phrase changes for the reader
186
+
187
+ The reader is an API consumer planning their next integration step. They do not run the database, they do not know the trigger or migration names, and they have never seen the table that backs a resource. Frame every change in the words the consumer would use when describing it to a teammate.
188
+
189
+ 1. Lead with what the consumer can now do, or what they no longer need to do.
190
+
191
+ 2. Skip database and infrastructure terms. Words like `seed`, `seeded`, `row`, `trigger`, `materialized`, `backfill`, `cascade table`, `enqueued`, `dispatcher`, `pool` belong to the implementation. Reach for `prefilled with the default values`, `record`, `behind the scenes`, `applied to every existing organization at the release`.
192
+
193
+ 3. Skip naming internal subsystems. The reader does not know which Go package or SQL function owns the change. Talk about the API surface and the resulting behavior.
194
+
195
+ 4. Skip implementation rationale unless the consumer needs it to integrate. The reader does not need to know that a function moved or that two write paths merged into one; they need to know which endpoint to call.
196
+
197
+ 5. Match the tone of a release note that a product team sends to integrators: short, clear, friendly, anchored on what the consumer sees in their own client.
198
+
199
+ A sentence that names an internal column, a function name, or a migration number is a sign the section is written for a contributor, not a consumer. Rewrite it in the consumer's words.
200
+
201
+ ## How to phrase removals
202
+
203
+ A removal lands in one of three buckets. Pick the bucket; the wording follows.
204
+
205
+ 1. Breaking removal of a public capability — keep the heading short, name the capability, write a short paragraph that names what the consumer must change to keep working, and bold the word `breaking`.
206
+
207
+ 2. Gated capability that is held back from public exposure — say it is held back and why ("held back until end-to-end testing finishes"), do not say "removed". A held-back capability is coming back; calling it removed is misleading.
208
+
209
+ 3. Capability the consumer never saw (debug, internal, deprecated-from-day-one) — do not write a line at all. It does not belong in the changelog.
210
+
211
+ ## How to phrase examples and error responses
212
+
213
+ The reader cares that the docs Try-It and the response panels show meaningful bodies, not that some example file lives at a repo path.
214
+
215
+ 1. Write what the reader sees in the docs: "Every endpoint shows a real captured response body for `200`, `400` and `403` in the docs panel" — not "examples live under `openapi_definition/examples/...`".
216
+
217
+ 2. Status codes belong inside backticks. Repo paths to example files do not appear at all.
218
+
219
+ 3. If the change is the addition of error response bodies on the docs site, that is one line. Not three list items naming the file layout.
220
+
221
+ ## Rendered shape
222
+
223
+ 1. Page H1 reads from the top level `title` of the highest version entry, so every entry file uses the same string.
224
+
225
+ 2. Each entry is a row of `[version pill] [data.title] [chevron]`. Clicking the row expands the body.
226
+
227
+ 3. The first entry expands by default. A direct hash like `/path#v1.4.2` expands that entry and scrolls to it.
228
+
229
+ 4. The right rail TOC lists every version as a level 2 heading. The left sidebar adds one child entry per version under the collection page, each pointing at the hash anchor.
@@ -0,0 +1,23 @@
1
+ # Chunk cycles
2
+
3
+ The workshop build code splits every story and component entry into its own chunk. When a module bundled into shared code statically imports a component that also gets its own chunk, and that component imports anything from the shared side, Rollup emits chunks that import each other. The source module graph has no cycle, dev serves modules unbundled and works, and the deployed static site throws `Uncaught ReferenceError: Cannot access 'x' before initialization` while evaluating whichever cyclic chunk loads first. One crash rejects the whole import chain, so every story loading either chunk renders nothing.
4
+
5
+ ## The guard
6
+
7
+ Every nimpress build runs the `nimpress:chunk-cycle-guard` plugin over the emitted bundle, for the site and for each harness system. A cycle fails the build and prints each cycle as a chunk path with the source module behind every chunk. `nimpress lint` builds, so the guard runs there and in CI with no configuration.
8
+
9
+ ## The rule for library code
10
+
11
+ 1. Registries, menus, and any module shared across many components load components through dynamic imports, in vue via `defineAsyncComponent(() => import('...'))`, never through static imports.
12
+
13
+ 2. A component may statically import other components it renders. The cycle needs both directions: shared code importing the component and the component importing shared code. Breaking the registry side is always the fix.
14
+
15
+ 3. Dynamic imports defer evaluation past module init, so no cycle forms and the component chunk loads on demand.
16
+
17
+ ## When the guard fires
18
+
19
+ 1. Read the printed cycle. Each line names the emitted chunks and the source module each chunk was built from.
20
+
21
+ 2. Find the shared module in the cycle that statically imports a component, switch that import to a dynamic one, and rebuild.
22
+
23
+ 3. Never widen chunks or force chunk grouping to silence the guard; that hides the coupling instead of removing it.
@@ -0,0 +1,152 @@
1
+ # Component modules
2
+
3
+ The component workshop surface in nimpress. One system per component library, one page per component, stories as executable files beside the page. This rule is the contract for authoring and for the pipeline.
4
+
5
+ ## Config
6
+
7
+ The `modules` array in the nimpress config declares systems:
8
+
9
+ ```ts
10
+ modules: [
11
+ {
12
+ name: 'nimtech',
13
+ framework: 'vue',
14
+ source: './src/components',
15
+ package: '@nimling/components-nimtech',
16
+ port: 6161,
17
+ css: ['./src/assets/style.css'],
18
+ harness: './docs/components/harness.vue'
19
+ }
20
+ ]
21
+ ```
22
+
23
+ 1. `framework` is `vue` or `svelte`. One framework per system.
24
+
25
+ 2. `source` is the component source root for local mode. `package` is the released npm package for package mode. Source wins when the component resolves; package is the fallback.
26
+
27
+ 3. `css` entries load inside the harness iframe. Point them at the token and theme sheets the components need.
28
+
29
+ 4. The harness mounts the component bare. Everything a library needs around its components is declared, never discovered: `setup` points at a module bootstrapping the app, its default export carries `install(app)` run on every created app before mount and `companion`, a component rendered beside every story; a svelte system exports a function run once. Overlay roots, plugin installs, and providers all come through `setup` or the harness.
30
+
31
+ 4.1. `harness` points at the system harness component wrapping every story, a `.ts`, `.tsx`, `.vue`, or `.svelte` path. There is no filename discovery; a harness exists only where the config or a story declares it.
32
+
33
+ 4.2. The frame is a composition of harness primitives imported from `@nimtech/nimpress/harness/vue` or `@nimtech/nimpress/harness/svelte`. `ComponentHarness` is the root, `ComponentStory` is the mount point where nimpress renders the component under test, `ComponentHarnessEffects` wraps its children and applies the toolbar drop shadow, `ComponentHarnessOverlay` mounts the system overlay root. The default composition is `ComponentHarness` wrapping `ComponentHarnessEffects` around `ComponentStory` with `ComponentHarnessOverlay` beside it, and it runs when a system declares no harness.
34
+
35
+ 4.3. A custom harness imports the primitives and arranges them freely, wrapping `ComponentStory` in a layout, adding providers, reordering, or omitting a piece. nimpress feeds the component, its props, slots, and overlay into the tree, so `ComponentStory` always resolves it. The checkerboard background, zoom, and vision filters are workshop chrome around the iframe, not harness primitives, and stay on the toolbar.
36
+
37
+ 5. `port` pins the harness dev server. Pin distinct ports when several repos run side by side.
38
+
39
+ 6. Consumer `vite` config flows into the harness, so aliases like `@` work inside components and stories.
40
+
41
+ 7. `visibility: dev-only` keeps a system in `nimpress dev` and out of the built bundle.
42
+
43
+ 8. Sizing is never configured here. The frame centers whatever the story mounts, so a component that needs a width owns it in the harness component or in a wrapping div in the story's own render.
44
+
45
+ ## The component page
46
+
47
+ 1. A folder under `docs/components/<Group>/<Component>/` holds exactly one md file with `type: component`; the build refuses a second one. The folder becomes the sidebar parent, the page appears inside it under its title, stories follow as siblings.
48
+
49
+ 2. Frontmatter contract: `title`, `type: component`, `data.system`, `data.component`, optional `data.package`, optional `data.file` when the source layout deviates from `<Component>/<Component>.<ext>`.
50
+
51
+ 2.1. The overview body opens with the `## Usage` import statement, then prose describing the component, what it is for, its behaviors, its slots and events. Import and create scaffold the usage section; the description is authored by hand and is expected on every component. The live preview frame renders below the body automatically, followed by the CLAUDE.md section; neither is written in the markdown.
52
+
53
+ 3. Grouping is physical or declared: the group folder above the component folder groups by location, and a top level `sidebar` frontmatter block groups by declaration without changing the folder or the URL. A declared group sits at the outer level, sibling to the physical group folders, standing in for the page's parent folder in the sidebar.
54
+
55
+ 3.1. The `sidebar` block: required `name` as the verbatim group label, optional `icon` for an ascii icon before the label, optional `style` for inline css on the group row, optional `path` overriding the group route. Latest definition among members wins. A `name` matching the page's own parent folder decorates that physical group instead. On a folder `index.md` the block decorates the folder's own sidebar entry rather than nesting a group.
56
+
57
+ 4. A CLAUDE.md in the component source folder renders on the page and is editable in local dev only.
58
+
59
+ ## Stories
60
+
61
+ 1. A story is a `.story.tsx` file beside the component page, so html sits directly in the render; `.story.ts` also loads. Import `vueStory` or `svelteStory` from `@nimtech/nimpress/story` and default export the definition.
62
+
63
+ 2. Name resolution order: `// story: <name>` comment frontmatter, then the `name` field, then the file name with underscores read as spaces, so `With_Controls.story.ts` names the story `With Controls`.
64
+
65
+ 2.1. A story decorates its own sidebar row with an optional `sidebar` object, `sidebar: { name, icon, style }`, the same shape as the frontmatter `sidebar` block. `name` overrides the row label, `icon` is an ascii glyph rendered before it, `style` is inline css on the row. The story link stays keyed on the resolved story name, so only the label changes, never the url.
66
+
67
+ 2.2. Story `name`, `props`, `slots`, and `sidebar` are read by parsing the story file as text, never by executing the module, so their values must be inline literals. An imported or computed value does not resolve; a `props` or `sidebar` that references an import comes through empty. A story that needs an imported value, a raw html or css file loaded with `?raw`, uses `render`, which runs in the browser where imports resolve.
68
+
69
+ 3. A value story carries `props` and optional `slots`; the workshop controls seed from it and drive the component live over the iframe channel with base64 encoded props in the url.
70
+
71
+ 4. An executable story carries `render`, a function returning a mount definition. Vue: an options object with `components`, `setup`, `template`. The harness runs it verbatim; controls do not apply. A story is self contained: it imports the component and holds its own fixture data inline. There is no shared story fixture folder.
72
+
73
+ 4.1. A value story nests itself in a per story harness with the `harness` field: `vueStory({ harness: BlockEditorHarness, props })`. The harness is a component that renders `<slot />` where `ComponentStory` mounts, and it folds inside the system harness as `SystemHarness > StoryHarness > ComponentStory`. A render story includes the shared harness component directly in its own markup instead and owns its sizing there, `<div style="width: 210mm">` around the harness when the story needs a width. Shared setup that a family of stories needs, a wrapping editor, providers, or seeded context, lives in one harness component the stories import from their group folder, a `harness.vue` beside the group's pages.
74
+
75
+ 5. Props and controls are one structure loaded from the schema file beside `index.md`, `schema.json` or `schema.yml`, one of the two. The schema is the read path: the workshop controls, validation, and mock selection all come from it. Object schemas resolve into nested member controls, arrays into row editors, enums into selects. Story `props` are plain data destructured into that tree; a story never defines controls. The schema carries `properties`, `required`, `slots`, `emits`, and a `mock` name per member.
76
+
77
+ 6. The schema is seeded from the component types by import and create, and from there the file is authored: defaults, enums, descriptions, mocks, and narrowing live in the schema, not in the component. `nimpress modules update [component]` upserts source changes into it: new props, slots, and emits are added, a changed type shape refreshes on existing members, authored fields are never overwritten, an empty description fills from a JSDoc or line comment above the type member, and a member gone from the source stays in the file and is flagged so the author decides its removal.
78
+
79
+ 6.1. In `nimpress dev` the upsert runs automatically whenever a component under a page changes, and every pass warns about what the author should fix: props without a description, opaque types, and schema members without a source counterpart. The warnings coach the library toward fully documented and typed components.
80
+
81
+ 6.2. In local dev the json dialog carries a set defaults action that writes the current live control values into the schema file as the prop defaults, so defaults are found by testing in the workshop and persisted where they belong.
82
+
83
+ 7. A `data.schema` block in the page frontmatter carries the same schema structure inline and merges over the schema file as the page local last word, for small curations that do not warrant the file.
84
+
85
+ 8. The page `title` is the human readable display name; `data.component` is the technical identifier the harness resolves. They may differ freely.
86
+
87
+ ## Controls and kinds
88
+
89
+ 1. Control kinds derive from the schema: `text`, `number`, `boolean`, `select` from enums, `object` with nested member rows, `array` with item rows, `record` with key value entries, `function` for callable props, `json` only for opaque leaves. Enum options render as chips in the control info; a long option list collapses to one line that expands in a hover popup.
90
+
91
+ 2. The schema to control walk expands nested members to a depth of 12, held in `SCHEMA_CONTROL_MAX_DEPTH` in `src/modules/parse/typeMembers.ts`. That covers an array of groups holding lanes holding entries holding a style block holding colors, the deepest real component tree. A member past the bound becomes a `json` leaf and the opaque warning names it, so a warning on a member that carries `properties` or `items` is the bound speaking, not the schema.
92
+
93
+ 3. Function props and events hold real code: the workshop stores `{ __nimpressFn: "<source>" }` values edited in a CodeMirror editor, the harness compiles the source with the Function constructor and runs it on every call, firings counting on the event row and printing in the console panel. The default mock stub logs its arguments.
94
+
95
+ 4. Every event renders as its own control row under the events header: the handler source sits in an always visible code editor spanning the row, mock resets it to the logging stub, clear detaches the event, a chevron hides the editor per row. A story starts with logging stubs attached to all events. A default button at the events header resets every handler to the logging stub at once.
96
+
97
+ 5. Mock resolves from the schema through the `@nimtech/nimpress/mock` named exports: the `mock` name stored per member picks the function, `mockEmail`, `mockParagraph`, `mockInt`, `mockOption`, `mockEvent`, and the rest, all star wars flavored. Reclicking regenerates via a seed, and option picks exclude values already present so a reclick lands on a fresh option. Per row mock fills one prop; the panel header mock fills everything empty including handlers.
98
+
99
+ 6. Control values persist to localStorage per system, component, and story. Clear per row or clear all from the panel header; the panel header also carries default, resetting every control to its schema default and every slot to its declared default. Once the workshop has pushed props to the frame, the pushed set alone drives the component and story props and required defaults no longer fill in underneath, so a cleared prop renders truly empty. The json dialog in the panel head shows the live values as one editable json object plus the schema, with copy and populated counts.
100
+
101
+ ## The workshop layout
102
+
103
+ 1. The shell is the martin special: absolute inset zero root, `overflow: hidden` everywhere structural, one unconditional grid, top bar row then sidebar and page columns, the document never scrolls, scrolling lives only inside panels, the sidebar, and the component's own frame.
104
+
105
+ 2. The workshop grid has a bottom slot and a right slot that collapse to zero when empty. The props panel and the frame console panel each toggle from the toolbar and dock to either slot; sharing a slot splits it. Drag areas are unstyled divs straddling the panel borders.
106
+
107
+ 3. The console panel streams the iframe's own console, every level plus window errors and unhandled rejections, forwarded over the message bridge, filterable and clearable. An input at the bottom evaluates js inside the frame console style, enter runs, arrows walk the history, input and result echo as `>` and `<` entries.
108
+
109
+ 4. The harness iframe document is locked, `#host` is absolute inset zero with `overflow: auto`; a natural sized component centers on the checkered background, an oversized one scrolls inside the frame, and one set to full width and height fills the frame exactly at any zoom.
110
+
111
+ 5. The toolbar shadow toggle drops a shadow on the component inside the frame and an inset shadow on the frame itself, tuned per frame theme so both read on light and dark.
112
+
113
+ ## Embedding a component in any page
114
+
115
+ The `:::component` directive renders a live component inline in any markdown page through the same harness:
116
+
117
+ ```markdown
118
+ :::component {"component":"MarButton","props":{"label":"Save"},"height":"12em"}
119
+ :::
120
+ ```
121
+
122
+ 1. `component` alone suffices when one system is configured; `system` names it otherwise. `story` selects a story file by its base name, `props` and `slots` travel base64 encoded, `height` sizes the frame.
123
+
124
+ 2. `:::component MarButton` is the shorthand for the default story with default controls.
125
+
126
+ ## CLI
127
+
128
+ Every modules subcommand takes `--system=<name>`; it is required only when several systems are configured.
129
+
130
+ 1. `nimpress modules import` walks the system source, mines storybook CSF files for groups from meta titles, named stories with args, argTypes merged into the seeded schema, and render stories ported executable, then fills storyless components with generated default stories. A mined story whose name equals the component becomes the Default story, written as `default.story.tsx` with the name `Default`. A data module a render story imports copies in beside the story so the component folder stays self contained. Flags: `--source=`, `--stories=<extra csf dir>`, `--match=<component name regex>`, `--select` for interactive picking. Reimports upsert the schema and never clear authored content.
131
+
132
+ 2. `nimpress modules import <file> --name=` registers one external component file into the system.
133
+
134
+ 3. `nimpress modules story [component] [--framework=]` writes `default.story.tsx` named `Default` for every storyless component page, one uniform entry story per component.
135
+
136
+ 4. `nimpress modules create <Component>` scaffolds the dedicated component folder: Overview `index.md`, `default.story.tsx`, and the seeded `schema.json`.
137
+
138
+ 5. `nimpress modules update [component] [--system=]` upserts the schema for one component page, or every page of a system when no component is named. The ref is the component name when unique across systems, else the component file path; `create --component=<ref> --schema` runs the same upsert for one page. This is the refresh loop after changing component types, since the workshop reads the schema, not the source.
139
+
140
+ 6. `nimpress modules lint [--system=]` checks framework purity of stories and component files, that every page carries at least one story, exactly one schema file present and parseable beside every `index.md`, value story props against the schema, and the schema structurally against the component source: every source member exists in the schema, schema members without a source counterpart are flagged, an authored type conflicting with the source type fails, and an authored enum must fit the source type and stay inside a source union. Authored narrowing, an enum over a string, is legal. Harness and render stories are exempt from the prop check. Coaching warnings for undocumented props and opaque types print without failing the run. Run it after touching stories, schemas, or component types.
141
+
142
+ 7. `nimpress lint` validates structure, folder and file naming, file combinations, frontmatter, and every import in content code files, then builds to verify the output compiles. The import pipeline lives in the nimpress CLI; a consumer repo carries no generator or import scripts.
143
+
144
+ 8. Import and create seed the schema beside `index.md` when none exists. Opaque prop types log warnings naming the component, prop path, and type; fix them by authoring the member in the schema with enum or properties, or by documenting the type in the component file or a sibling `.types.ts` and running update.
145
+
146
+ 8.1. `unknown` is never the fix for an opaque member. Neither the source type nor an authored `tsType` is widened to `unknown` to silence the warning; the member gets its real shape, an enum, properties, typed items, or a real interface in the source. A prop that only exists to hold untyped data is removed instead.
147
+
148
+ 9. `nimpress modules dev [--system=]` runs harness servers. `nimpress dev` starts them all beside the docs.
149
+
150
+ 10. `nimpress modules build [--system=]` emits static harness bundles into `dist/_components/<system>/`; `nimpress build` runs it for every system whose visibility is not `dev-only`.
151
+
152
+ 11. `nimpress export [--target=] [--out=]` collects pages marked with the `export:` frontmatter header into `.nimpress` for the docs-sync pipeline, rewriting component pages to package mode. The `docs-export` action is the pipeline twin; see the docs-sync rule.
@@ -0,0 +1,118 @@
1
+ # DBML diagrams
2
+
3
+ The database diagram surface in nimpress. One DBML source, one interactive entity relationship diagram, either inline in a page or as a page of its own.
4
+
5
+ ## The pieces
6
+
7
+ 1. `@dbml/core` parses the DBML at build time inside the plugin. The browser never sees DBML, only the diagram model.
8
+
9
+ 2. `@xyflow/svelte` renders the model as a canvas of nodes and edges, lazily imported by `DBMLBlock` together with the node components.
10
+
11
+ 3. `DbmlTable.svelte` and `DbmlNote.svelte` are the node components. Every table card, column row, flag, and sticky note is nimpress markup styled from `tokens.css`, so the diagram follows the site theme.
12
+
13
+ 4. `src/dbml/erd.ts` is the converter. Every mapping decision from DBML to the diagram, including the table positions and which side of a table an edge leaves from, lives there and nowhere else.
14
+
15
+ ## Inline fence
16
+
17
+ A ` ```dbml ` fence renders inline in any page that hydrates markdown: `doc`, `changelog`, and `roadmap`.
18
+
19
+ ````md
20
+ ```dbml
21
+ Table users {
22
+ id uuid [pk]
23
+ email varchar(255) [not null, unique]
24
+ }
25
+ ```
26
+ ````
27
+
28
+ 1. The info line takes an optional JSON object. `height` is the only field, `520px` by default. Pick it from the table count: the reader pans by dragging whatever the tables do not cover, so a cramped frame has nothing to grab.
29
+
30
+ 2. The plugin rewrites the fence to `<div class="np-dbml" data-schema="…">` carrying the base64 diagram model, and the page mounts `DBMLBlock` over it.
31
+
32
+ 3. An inline diagram mounts behind a click to explore shield so the frame never captures page scrolling. Clicking it activates the frame and swaps the shield for a gesture hint. `DbmlPage` passes `activateOnMount` because there the diagram is the page.
33
+
34
+ ## Page type
35
+
36
+ `type: dbml` turns one `.dbml` file into a full page viewer.
37
+
38
+ ```yaml
39
+ ---
40
+ title: Bookable schema
41
+ type: dbml
42
+ spec: ./bookable.dbml
43
+ description: Every table the booking api reads and writes.
44
+ data:
45
+ eyebrow: Database
46
+ lead: Rooms, desks, and bookings.
47
+ download: Download the schema
48
+ fullscreen: Open fullscreen
49
+ ---
50
+ ```
51
+
52
+ 1. `spec` is required and resolves relative to the markdown file. Lint fails when it is missing or does not resolve.
53
+
54
+ 2. The page opens with a hero band. Title, description, `data.eyebrow`, `data.lead`, `data.logo`, `data.banner`, and `data.align` shape it, and the markdown body renders under them. Every one of those sits in the same centered content column as a doc page, so only the diagram is full width.
55
+
56
+ 3. The band carries the page buttons. `data.download` labels the button handing over the `.dbml` source, `data.fullscreen` labels the button opening the diagram fullscreen, and `false` on either removes that button. `data.actions` adds links, each with `text`, `link`, and a `variant` of `primary`, `secondary`, or `ghost`.
57
+
58
+ 4. The page has no right rail. The diagram runs edge to edge under the band with no padding, no margin, and no rounded corners, and fills the viewport below the site header. `data.height` overrides that height.
59
+
60
+ 5. The `.dbml` file lands in the build output beside the page, so readers can open the source.
61
+
62
+ ## The interaction contract
63
+
64
+ Pan by dragging empty canvas or by the minimap. Zoom with the scroll wheel, with the control buttons, or by the minimap. Drag a table to move it, which is a view only rearrangement and is never written back to the `.dbml` file.
65
+
66
+ Click a column carrying a link or a foreign key to follow it. A foreign key column centers the table it points at and selects it. A markdown link in the column note overrides that: a target naming a table centers that table, and any other target is a page link, navigating for a site path, opening a new tab for a full url, and scrolling for a `#anchor`.
67
+
68
+ The starting layout comes from `src/dbml/erd.ts`, which positions the tables at build time and separates any two cards that would touch. Cards never overlap on first paint. A schema that still reads badly is fixed there or in the source order, not by asking readers to drag.
69
+
70
+ Scroll zoom is armed only once the frame is active, so an inline diagram never captures page scrolling before the reader clicks it.
71
+
72
+ ## What the converter maps
73
+
74
+ 1. Tables, columns, data types, defaults, and notes.
75
+
76
+ 2. Primary keys, composite primary keys from an `indexes` block marked `pk`, unique columns, not null columns, and auto increment.
77
+
78
+ 3. Refs as relationship lines drawn between the two columns themselves, entering the side of each table that faces the other. The endpoint with cardinality `1` is the parent; a one to one ref takes the right hand side as the parent. A to many line carries an arrow head at the child end.
79
+
80
+ 4. Named and unique indexes.
81
+
82
+ 5. `headercolor` as the table header color.
83
+
84
+ 6. Standalone `Note` blocks as sticky notes placed to the right of the tables.
85
+
86
+ 7. Schema qualified table names when the schema is not `public`.
87
+
88
+ 8. A markdown link inside a column note as the column's click target, its label as a pill on the row, and the remaining note text as the hover title.
89
+
90
+ ## Authoring the source
91
+
92
+ 1. One `.dbml` file per page. Do not split a schema across files and stitch it in the markdown.
93
+
94
+ 2. Write a `Note` on every table. It renders as a second line inside the table header, so a schema with notes reads without hovering anything.
95
+
96
+ 3. Write column notes too. A column note becomes the hover title on that row.
97
+
98
+ 3.1. Draw a `jsonb` payload as a table of its own and link the column at it from the column note, `[booking_answers](booking_answers)`. The payload table carries a `Note` saying it has no table of its own in the database.
99
+
100
+ 4. Name every index. An unnamed index renders under a generated label in the table footer.
101
+
102
+ 5. Set `headercolor` on a table to group it visually. Tables without one take the brand color.
103
+
104
+ ## What is wrong if you see it
105
+
106
+ 1. DBML parsed in the browser. Parsing is a build time step; the page payload carries the diagram model.
107
+
108
+ 2. A component importing `@xyflow/svelte` from shared code, or `DbmlTable.svelte` and `DbmlNote.svelte` imported statically. All three are lazy imports inside `DBMLBlock`, see the chunk cycles rule.
109
+
110
+ 3. Mapping or layout logic added to `DBMLBlock` or a page renderer. It belongs in `src/dbml/erd.ts`.
111
+
112
+ 3.1. Colors written into the node components as literals. The cards read `tokens.css` so light and dark both work.
113
+
114
+ 4. A `type: dbml` page with a long markdown body. Move the prose to a sibling `doc` page and link to it.
115
+
116
+ 4.1. A `type: dbml` page whose header text runs the full window width. The band is centered in the content column; only the diagram is full width.
117
+
118
+ 5. A schema pasted into a fenced ` ```sql ` block as a stand in for a diagram.
@@ -0,0 +1,47 @@
1
+ # Deploy
2
+
3
+ The release pipeline for `@nimtech/nimpress`. Run from the repo root.
4
+
5
+ ## Steps in order
6
+
7
+ 1. `just build` to compile the library into `dist/`. Fail the deploy if the build fails.
8
+
9
+ 2. `just check` to run `svelte-check` and `tsc`. Fail the deploy if type checks fail.
10
+
11
+ 3. Stage and commit every changed file with a single sentence summarizing the release. Conventional commits are not required, the wording should describe the user visible change.
12
+
13
+ 4. `git push` to `origin/main`.
14
+
15
+ 5. `just deploy` to invoke `sbump` which bumps the patch version, writes the tag, and pushes both.
16
+
17
+ ## What the tag triggers
18
+
19
+ 1. `Generate and publish nimpress` builds the library, runs the tests, and publishes the package to npm under the `@nimtech` scope. It is public, so a consumer installs it with no registry configuration and no token.
20
+
21
+ 2. `Release nimpress actions` validates the Go actions and moves the major tag onto the release.
22
+
23
+ 3. `Publish the docs site` builds `docs/` with the freshly built library and deploys it to GitHub Pages at `nimling.github.io/nimpress`. The site build writes into `dist/site` so it never clears the library output, and the `base` field in `nimpress.config.json` is what makes the project page path resolve.
24
+
25
+ A release that changes a user visible surface carries its changelog entry in `docs/changelog/` in the same commit, one file per release. The entry contract is in `changelog-entries.md`.
26
+
27
+ ## Variants
28
+
29
+ 1. Minor release: replace step 5 with `just deploy-minor`.
30
+
31
+ 2. Major release: replace step 5 with `just deploy-major`.
32
+
33
+ ## Never
34
+
35
+ 1. Never run `just deploy` without a clean build and clean type check first. A broken release is harder to roll back than to prevent.
36
+
37
+ 2. Never push directly to a published version. `sbump` increments the version. Manual edits to `package.json` `version` are not how this repo ships.
38
+
39
+ 3. Never bypass GPG signing or pre commit hooks. If the hook fails, fix the issue and retry.
40
+
41
+ 4. Never deploy with uncommitted changes in the working tree. `sbump` writes a commit, the tree must already be at the state you want to ship.
42
+
43
+ 5. Never run `pnpm publish` directly. The `just deploy` flow is the only sanctioned path.
44
+
45
+ ## When the user says "deploy"
46
+
47
+ The `deploy` skill in `.claude/skills/deploy.md` lays out the exact commands. Execute it end to end, stopping only if a step fails. Surface errors verbatim, do not paper over them.