@stacksjs/defaults 0.70.206 → 0.70.208

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 (408) hide show
  1. package/TODO.md +21 -0
  2. package/ai/AGENTS.md +79 -0
  3. package/ai/README.md +45 -0
  4. package/ai/claude/launch.json +12 -0
  5. package/ai/skills/stacks-actions/SKILL.md +128 -0
  6. package/ai/skills/stacks-ai/SKILL.md +276 -0
  7. package/ai/skills/stacks-alias/SKILL.md +94 -0
  8. package/ai/skills/stacks-analytics/SKILL.md +91 -0
  9. package/ai/skills/stacks-api/SKILL.md +738 -0
  10. package/ai/skills/stacks-arrays/SKILL.md +277 -0
  11. package/ai/skills/stacks-auth/SKILL.md +422 -0
  12. package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
  13. package/ai/skills/stacks-brandkit/SKILL.md +811 -0
  14. package/ai/skills/stacks-browse/SKILL.md +127 -0
  15. package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
  16. package/ai/skills/stacks-browser/SKILL.md +145 -0
  17. package/ai/skills/stacks-buddy/SKILL.md +676 -0
  18. package/ai/skills/stacks-build/SKILL.md +163 -0
  19. package/ai/skills/stacks-cache/SKILL.md +125 -0
  20. package/ai/skills/stacks-calendar/SKILL.md +113 -0
  21. package/ai/skills/stacks-chat/SKILL.md +371 -0
  22. package/ai/skills/stacks-cli/SKILL.md +112 -0
  23. package/ai/skills/stacks-cloud/SKILL.md +192 -0
  24. package/ai/skills/stacks-cms/SKILL.md +217 -0
  25. package/ai/skills/stacks-collections/SKILL.md +82 -0
  26. package/ai/skills/stacks-commerce/SKILL.md +114 -0
  27. package/ai/skills/stacks-composables/SKILL.md +161 -0
  28. package/ai/skills/stacks-config/SKILL.md +144 -0
  29. package/ai/skills/stacks-configuration/SKILL.md +154 -0
  30. package/ai/skills/stacks-cron/SKILL.md +123 -0
  31. package/ai/skills/stacks-crosswind/SKILL.md +190 -0
  32. package/ai/skills/stacks-dashboard/SKILL.md +173 -0
  33. package/ai/skills/stacks-database/SKILL.md +286 -0
  34. package/ai/skills/stacks-datetime/SKILL.md +269 -0
  35. package/ai/skills/stacks-dependencies/SKILL.md +111 -0
  36. package/ai/skills/stacks-deploy/SKILL.md +102 -0
  37. package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
  38. package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
  39. package/ai/skills/stacks-design-output/SKILL.md +55 -0
  40. package/ai/skills/stacks-design-soft/SKILL.md +156 -0
  41. package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
  42. package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
  43. package/ai/skills/stacks-desktop/SKILL.md +78 -0
  44. package/ai/skills/stacks-development/SKILL.md +298 -0
  45. package/ai/skills/stacks-dns/SKILL.md +149 -0
  46. package/ai/skills/stacks-docs/SKILL.md +109 -0
  47. package/ai/skills/stacks-email/SKILL.md +236 -0
  48. package/ai/skills/stacks-enums/SKILL.md +100 -0
  49. package/ai/skills/stacks-env/SKILL.md +181 -0
  50. package/ai/skills/stacks-error-handling/SKILL.md +137 -0
  51. package/ai/skills/stacks-events/SKILL.md +228 -0
  52. package/ai/skills/stacks-faker/SKILL.md +158 -0
  53. package/ai/skills/stacks-git/SKILL.md +158 -0
  54. package/ai/skills/stacks-guard/SKILL.md +98 -0
  55. package/ai/skills/stacks-health/SKILL.md +75 -0
  56. package/ai/skills/stacks-http/SKILL.md +316 -0
  57. package/ai/skills/stacks-i18n/SKILL.md +198 -0
  58. package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
  59. package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
  60. package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
  61. package/ai/skills/stacks-investigate/SKILL.md +119 -0
  62. package/ai/skills/stacks-jobs/SKILL.md +136 -0
  63. package/ai/skills/stacks-lint/SKILL.md +184 -0
  64. package/ai/skills/stacks-listeners/SKILL.md +99 -0
  65. package/ai/skills/stacks-logging/SKILL.md +78 -0
  66. package/ai/skills/stacks-mail/SKILL.md +120 -0
  67. package/ai/skills/stacks-middleware/SKILL.md +249 -0
  68. package/ai/skills/stacks-migrations/SKILL.md +105 -0
  69. package/ai/skills/stacks-models/SKILL.md +267 -0
  70. package/ai/skills/stacks-new-feature/SKILL.md +206 -0
  71. package/ai/skills/stacks-notifications/SKILL.md +210 -0
  72. package/ai/skills/stacks-objects/SKILL.md +205 -0
  73. package/ai/skills/stacks-office-hours/SKILL.md +134 -0
  74. package/ai/skills/stacks-orm/SKILL.md +342 -0
  75. package/ai/skills/stacks-path/SKILL.md +118 -0
  76. package/ai/skills/stacks-payments/SKILL.md +373 -0
  77. package/ai/skills/stacks-plan-review/SKILL.md +152 -0
  78. package/ai/skills/stacks-plugins/SKILL.md +149 -0
  79. package/ai/skills/stacks-push/SKILL.md +376 -0
  80. package/ai/skills/stacks-query-builder/SKILL.md +128 -0
  81. package/ai/skills/stacks-queue/SKILL.md +559 -0
  82. package/ai/skills/stacks-realtime/SKILL.md +442 -0
  83. package/ai/skills/stacks-redesign/SKILL.md +182 -0
  84. package/ai/skills/stacks-registry/SKILL.md +58 -0
  85. package/ai/skills/stacks-repl/SKILL.md +80 -0
  86. package/ai/skills/stacks-retro/SKILL.md +122 -0
  87. package/ai/skills/stacks-review/SKILL.md +135 -0
  88. package/ai/skills/stacks-router/SKILL.md +231 -0
  89. package/ai/skills/stacks-routes/SKILL.md +105 -0
  90. package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
  91. package/ai/skills/stacks-scheduler/SKILL.md +363 -0
  92. package/ai/skills/stacks-search-engine/SKILL.md +112 -0
  93. package/ai/skills/stacks-security/SKILL.md +129 -0
  94. package/ai/skills/stacks-security-audit/SKILL.md +128 -0
  95. package/ai/skills/stacks-server/SKILL.md +458 -0
  96. package/ai/skills/stacks-shell/SKILL.md +62 -0
  97. package/ai/skills/stacks-slug/SKILL.md +87 -0
  98. package/ai/skills/stacks-sms/SKILL.md +385 -0
  99. package/ai/skills/stacks-socials/SKILL.md +303 -0
  100. package/ai/skills/stacks-storage/SKILL.md +576 -0
  101. package/ai/skills/stacks-strings/SKILL.md +416 -0
  102. package/ai/skills/stacks-stx/SKILL.md +176 -0
  103. package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
  104. package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
  105. package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
  106. package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
  107. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
  108. package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
  109. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
  110. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
  111. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
  112. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
  113. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
  114. package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
  115. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
  116. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
  117. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
  118. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
  119. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
  120. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
  121. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
  122. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
  123. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
  124. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
  125. package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
  126. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
  127. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
  128. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
  129. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
  130. package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
  131. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
  132. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
  133. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
  134. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
  135. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
  136. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
  137. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
  138. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
  139. package/ai/skills/stacks-testing/SKILL.md +129 -0
  140. package/ai/skills/stacks-tunnel/SKILL.md +125 -0
  141. package/ai/skills/stacks-types/SKILL.md +178 -0
  142. package/ai/skills/stacks-ui/SKILL.md +246 -0
  143. package/ai/skills/stacks-utils/SKILL.md +444 -0
  144. package/ai/skills/stacks-validation/SKILL.md +371 -0
  145. package/ai/skills/stacks-whois/SKILL.md +315 -0
  146. package/assets/world-map-data.ts +121 -0
  147. package/bootstrap.ts +114 -0
  148. package/docs/api-examples.md +46 -0
  149. package/docs/index.md +24 -0
  150. package/docs/markdown-examples.md +87 -0
  151. package/functions/analytics.ts +97 -0
  152. package/functions/api.ts +143 -0
  153. package/functions/auth.ts +208 -0
  154. package/functions/billing/payments.ts +149 -0
  155. package/functions/blog.ts +120 -0
  156. package/functions/cms/categorizables.ts +107 -0
  157. package/functions/cms/pages.ts +108 -0
  158. package/functions/cms/posts.ts +100 -0
  159. package/functions/cms/taggables.ts +102 -0
  160. package/functions/commerce/coupons.ts +128 -0
  161. package/functions/commerce/customers.ts +128 -0
  162. package/functions/commerce/gift-cards.ts +119 -0
  163. package/functions/commerce/orders.ts +112 -0
  164. package/functions/commerce/payments.ts +119 -0
  165. package/functions/commerce/products/categories.ts +123 -0
  166. package/functions/commerce/products/manufacturers.ts +123 -0
  167. package/functions/commerce/products/products.ts +123 -0
  168. package/functions/commerce/products/reviews.ts +123 -0
  169. package/functions/commerce/products/units.ts +123 -0
  170. package/functions/commerce/products/variants.ts +123 -0
  171. package/functions/commerce/shippings/delivery-routes.ts +123 -0
  172. package/functions/commerce/shippings/digital-deliveries.ts +119 -0
  173. package/functions/commerce/shippings/drivers.ts +123 -0
  174. package/functions/commerce/shippings/license-keys.ts +123 -0
  175. package/functions/commerce/shippings/shipping-methods.ts +123 -0
  176. package/functions/commerce/shippings/shipping-rates.ts +119 -0
  177. package/functions/commerce/shippings/shipping-zones.ts +119 -0
  178. package/functions/commerce/taxes.ts +119 -0
  179. package/functions/commerce/waitlist/products.ts +123 -0
  180. package/functions/commerce/waitlist/restaurants.ts +123 -0
  181. package/functions/craft.ts +107 -0
  182. package/functions/dashboard.ts +153 -0
  183. package/functions/inbox.ts +95 -0
  184. package/functions/jobs.ts +86 -0
  185. package/functions/monitoring/errors.ts +295 -0
  186. package/functions/queue.ts +111 -0
  187. package/functions/realtime/websockets.ts +96 -0
  188. package/functions/realtime.ts +52 -0
  189. package/functions/releases.ts +49 -0
  190. package/functions/search.ts +126 -0
  191. package/functions/toasts.ts +95 -0
  192. package/functions/uploads.ts +216 -0
  193. package/ide/.gitignore +4 -0
  194. package/ide/cursor/rules/auto-imports.mdc +574 -0
  195. package/ide/cursor/rules/code-style.mdc +12 -0
  196. package/ide/cursor/rules/documentation.mdc +9 -0
  197. package/ide/cursor/rules/error-handling-validation.mdc +11 -0
  198. package/ide/cursor/rules/key-conventions.mdc +11 -0
  199. package/ide/cursor/rules/project-structure.mdc +169 -0
  200. package/ide/cursor/rules/readme.mdc +318 -0
  201. package/ide/cursor/rules/syntax-formatting.mdc +9 -0
  202. package/ide/cursor/rules/testing.mdc +10 -0
  203. package/ide/cursor/rules/typescript.mdc +9 -0
  204. package/ide/cursor/rules/ui-styling.mdc +0 -0
  205. package/ide/dictionary.txt +397 -0
  206. package/ide/jetbrains/.fleet/settings.json +4 -0
  207. package/ide/jetbrains/.idea/.gitignore +5 -0
  208. package/ide/jetbrains/.idea/.name +1 -0
  209. package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
  210. package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
  211. package/ide/jetbrains/.idea/modules.xml +8 -0
  212. package/ide/jetbrains/.idea/stacks.iml +22 -0
  213. package/ide/jetbrains/.idea/vcs.xml +6 -0
  214. package/ide/jetbrains/.idea/webResources.xml +14 -0
  215. package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
  216. package/ide/jetbrains/installed.txt +4 -0
  217. package/ide/jetbrains/options/github-copilot.xml +5 -0
  218. package/ide/jetbrains/options/nodejs.xml +7 -0
  219. package/ide/jetbrains/options/templates.xml +124 -0
  220. package/ide/jetbrains/templates/JavaScript.xml +6 -0
  221. package/ide/jetbrains/templates/Stacks.xml +8 -0
  222. package/ide/vscode/.vscode/README.md +23 -0
  223. package/ide/vscode/.vscode/extensions.json +5 -0
  224. package/ide/vscode/.vscode/settings.json +176 -0
  225. package/ide/vscode/.vscode/stacks.code-snippets +181 -0
  226. package/ide/vscode/LICENSE.md +21 -0
  227. package/ide/vscode/README.md +36 -0
  228. package/ide/vscode/logo.png +0 -0
  229. package/ide/vscode/package.json +79 -0
  230. package/ide/zed/.zed/settings.json +139 -0
  231. package/lang/README.md +28 -0
  232. package/lang/de.yml +14 -0
  233. package/lang/en.yml +15 -0
  234. package/package.json +19 -3
  235. package/project/bootstrap +161 -0
  236. package/project/buddy +175 -0
  237. package/project/storage/framework/tsconfig.app.json +50 -0
  238. package/project/storage/framework/tsconfig.base.json +58 -0
  239. package/routes/core.ts +78 -0
  240. package/routes/dashboard-api.ts +233 -0
  241. package/routes/dashboard.ts +739 -0
  242. package/routes/email.ts +129 -0
  243. package/stores/[name].stx +16 -0
  244. package/stores/git.ts +71 -0
  245. package/stores/payment.ts +474 -0
  246. package/stores/queue.ts +43 -0
  247. package/stores/user.ts +29 -0
  248. package/stx-components-plugin.ts +25 -0
  249. package/styles/design-tokens.css +597 -0
  250. package/types/dashboard.ts +70 -0
  251. package/types/defaults.ts +438 -0
  252. package/vcs/github/CODE_OF_CONDUCT.md +83 -0
  253. package/vcs/github/CONTRIBUTING.md +187 -0
  254. package/vcs/github/EXPLANATIONS.md +41 -0
  255. package/vcs/github/FUNDING.yml +1 -0
  256. package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
  257. package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
  258. package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
  259. package/vcs/github/SECURITY.md +70 -0
  260. package/vcs/github/renovate.json +5 -0
  261. package/vcs/github/stale.yml +14 -0
  262. package/vcs/github/workflows/README.md +16 -0
  263. package/vcs/github/workflows/ci.yml +114 -0
  264. package/vcs/github/workflows/export-size.yml +25 -0
  265. package/vcs/github/workflows/labeler.yml +13 -0
  266. package/vcs/github/workflows/release.yml +219 -0
  267. package/views/[...all].stx +8 -0
  268. package/views/about.md +3 -0
  269. package/views/auth/login.stx +1 -0
  270. package/views/auth/signup.stx +85 -0
  271. package/views/dashboard/.discovered-models.json +542 -0
  272. package/views/dashboard/AUDIT.md +904 -0
  273. package/views/dashboard/[...all].stx +89 -0
  274. package/views/dashboard/access-tokens/index.stx +177 -0
  275. package/views/dashboard/actions/index.stx +129 -0
  276. package/views/dashboard/analytics/blog/index.stx +356 -0
  277. package/views/dashboard/analytics/browsers/index.stx +263 -0
  278. package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
  279. package/views/dashboard/analytics/commerce/web/index.stx +379 -0
  280. package/views/dashboard/analytics/countries/index.stx +289 -0
  281. package/views/dashboard/analytics/devices/index.stx +305 -0
  282. package/views/dashboard/analytics/events/index.stx +536 -0
  283. package/views/dashboard/analytics/index.stx +88 -0
  284. package/views/dashboard/analytics/marketing/index.stx +315 -0
  285. package/views/dashboard/analytics/pages/index.stx +271 -0
  286. package/views/dashboard/analytics/referrers/index.stx +319 -0
  287. package/views/dashboard/analytics/web/index.stx +356 -0
  288. package/views/dashboard/buddy/index.stx +198 -0
  289. package/views/dashboard/ci/index.stx +462 -0
  290. package/views/dashboard/cloud/index.stx +452 -0
  291. package/views/dashboard/commands/index.stx +295 -0
  292. package/views/dashboard/commerce/categories/index.stx +406 -0
  293. package/views/dashboard/commerce/coupons/index.stx +485 -0
  294. package/views/dashboard/commerce/customers/index.stx +410 -0
  295. package/views/dashboard/commerce/dashboard/index.stx +287 -0
  296. package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
  297. package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
  298. package/views/dashboard/commerce/delivery/drivers.stx +397 -0
  299. package/views/dashboard/commerce/delivery/index.stx +18 -0
  300. package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
  301. package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
  302. package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
  303. package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
  304. package/views/dashboard/commerce/delivery/tabs.ts +25 -0
  305. package/views/dashboard/commerce/gift-cards/index.stx +561 -0
  306. package/views/dashboard/commerce/manufacturers/index.stx +308 -0
  307. package/views/dashboard/commerce/orders/index.stx +247 -0
  308. package/views/dashboard/commerce/payments/index.stx +535 -0
  309. package/views/dashboard/commerce/pos/index.stx +653 -0
  310. package/views/dashboard/commerce/printers/devices/index.stx +273 -0
  311. package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
  312. package/views/dashboard/commerce/products/detail.stx +563 -0
  313. package/views/dashboard/commerce/products/index.stx +370 -0
  314. package/views/dashboard/commerce/reviews/index.stx +656 -0
  315. package/views/dashboard/commerce/reviews/index.ts +0 -0
  316. package/views/dashboard/commerce/taxes/index.stx +377 -0
  317. package/views/dashboard/commerce/units/index.stx +317 -0
  318. package/views/dashboard/commerce/variants/index.stx +319 -0
  319. package/views/dashboard/commerce/waitlist/products.stx +942 -0
  320. package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
  321. package/views/dashboard/components/index.stx +330 -0
  322. package/views/dashboard/composables/index.ts +19 -0
  323. package/views/dashboard/composables/useChart.ts +77 -0
  324. package/views/dashboard/composables/useNavigation.ts +99 -0
  325. package/views/dashboard/composables/useRole.ts +58 -0
  326. package/views/dashboard/composables/useTheme.ts +74 -0
  327. package/views/dashboard/content/authors/index.stx +171 -0
  328. package/views/dashboard/content/blog/index.stx +493 -0
  329. package/views/dashboard/content/categories/index.stx +169 -0
  330. package/views/dashboard/content/comments/index.stx +189 -0
  331. package/views/dashboard/content/dashboard.stx +383 -0
  332. package/views/dashboard/content/files/index.stx +706 -0
  333. package/views/dashboard/content/pages/index.stx +174 -0
  334. package/views/dashboard/content/posts/index.stx +269 -0
  335. package/views/dashboard/content/seo/index.stx +240 -0
  336. package/views/dashboard/content/tags/index.stx +167 -0
  337. package/views/dashboard/data/activity/index.stx +261 -0
  338. package/views/dashboard/data/dashboard/index.stx +116 -0
  339. package/views/dashboard/data/subscribers.stx +270 -0
  340. package/views/dashboard/data/teams.stx +275 -0
  341. package/views/dashboard/data/users.stx +279 -0
  342. package/views/dashboard/dependencies/index.stx +178 -0
  343. package/views/dashboard/deployments/[id].stx +234 -0
  344. package/views/dashboard/deployments/index.stx +9 -0
  345. package/views/dashboard/dns/index.stx +163 -0
  346. package/views/dashboard/environment/index.stx +283 -0
  347. package/views/dashboard/errors/index.stx +274 -0
  348. package/views/dashboard/forgot-password.stx +57 -0
  349. package/views/dashboard/functions/index.stx +159 -0
  350. package/views/dashboard/health/index.stx +248 -0
  351. package/views/dashboard/inbox/activity.stx +350 -0
  352. package/views/dashboard/inbox/index.stx +452 -0
  353. package/views/dashboard/inbox/settings.stx +290 -0
  354. package/views/dashboard/index.stx +252 -0
  355. package/views/dashboard/insights/index.stx +529 -0
  356. package/views/dashboard/jobs/[id].stx +430 -0
  357. package/views/dashboard/jobs/history.stx +222 -0
  358. package/views/dashboard/jobs/index.stx +214 -0
  359. package/views/dashboard/kanban/[id].stx +899 -0
  360. package/views/dashboard/kanban/index.stx +260 -0
  361. package/views/dashboard/layouts/default.stx +565 -0
  362. package/views/dashboard/layouts/guest.stx +10 -0
  363. package/views/dashboard/login.stx +48 -0
  364. package/views/dashboard/logs/index.stx +540 -0
  365. package/views/dashboard/mailboxes/index.stx +149 -0
  366. package/views/dashboard/management/permissions/index.stx +490 -0
  367. package/views/dashboard/marketing/campaigns/index.stx +604 -0
  368. package/views/dashboard/marketing/lists/index.stx +514 -0
  369. package/views/dashboard/marketing/reviews/index.stx +475 -0
  370. package/views/dashboard/marketing/social-posts/index.stx +560 -0
  371. package/views/dashboard/models/[model].stx +195 -0
  372. package/views/dashboard/models/index.stx +159 -0
  373. package/views/dashboard/monitoring/errors/index.stx +415 -0
  374. package/views/dashboard/notifications/dashboard.stx +383 -0
  375. package/views/dashboard/notifications/email.stx +336 -0
  376. package/views/dashboard/notifications/history.stx +373 -0
  377. package/views/dashboard/notifications/sms.stx +332 -0
  378. package/views/dashboard/packages/index.stx +195 -0
  379. package/views/dashboard/queries/[id].stx +381 -0
  380. package/views/dashboard/queries/history.stx +227 -0
  381. package/views/dashboard/queries/index.stx +335 -0
  382. package/views/dashboard/queries/slow.stx +318 -0
  383. package/views/dashboard/queue/index.stx +427 -0
  384. package/views/dashboard/realtime/index.stx +429 -0
  385. package/views/dashboard/register.stx +46 -0
  386. package/views/dashboard/releases/index.stx +389 -0
  387. package/views/dashboard/requests/index.stx +462 -0
  388. package/views/dashboard/serverless/index.stx +475 -0
  389. package/views/dashboard/servers/[id].stx +299 -0
  390. package/views/dashboard/servers/index.stx +483 -0
  391. package/views/dashboard/settings/billing.stx +50 -0
  392. package/views/dashboard/settings/index.stx +256 -0
  393. package/views/dashboard/settings/mail.stx +252 -0
  394. package/views/dashboard/stores/auth.ts +123 -0
  395. package/views/dashboard/stores/ci.ts +342 -0
  396. package/views/dashboard/stores/index.ts +129 -0
  397. package/views/dashboard/stores/kanban.ts +723 -0
  398. package/views/dashboard/stores/rbac.ts +350 -0
  399. package/views/dashboard/stx.config.ts +34 -0
  400. package/views/dashboard/teams/[id].stx +170 -0
  401. package/views/forum/category.stx +92 -0
  402. package/views/forum/index.stx +209 -0
  403. package/views/forum/new-topic.stx +113 -0
  404. package/views/forum/profile.stx +1 -0
  405. package/views/forum/topic.stx +143 -0
  406. package/views/hello-world/[name].stx +47 -0
  407. package/views/index.stx +21 -0
  408. package/views/system-tray/index.stx +147 -0
@@ -0,0 +1,62 @@
1
+ /** Grid placement for architecture IR (#8). Not auto-layout - fixed cell math only. */
2
+
3
+ export const DEFAULT_GRID = {
4
+ mode: 'grid',
5
+ origin: [40, 80],
6
+ cols: 4,
7
+ gapX: 30,
8
+ gapY: 40,
9
+ cellW: 130,
10
+ cellH: 64,
11
+ };
12
+
13
+ export function gridLayout(arch) {
14
+ const raw = arch.layout;
15
+ if (!raw || raw.mode !== 'grid') return null;
16
+ return { ...DEFAULT_GRID, ...raw };
17
+ }
18
+
19
+ export function resolveComponentPos(component, grid) {
20
+ if (Array.isArray(component.pos) && component.pos.length === 2) {
21
+ return component.pos;
22
+ }
23
+ if (!grid) return [NaN, NaN];
24
+ if (!Number.isInteger(component.row) || !Number.isInteger(component.col)) {
25
+ return [NaN, NaN];
26
+ }
27
+ const [ox, oy] = grid.origin;
28
+ const stepX = grid.cellW + grid.gapX;
29
+ const stepY = grid.cellH + grid.gapY;
30
+ return [ox + component.col * stepX, oy + component.row * stepY];
31
+ }
32
+
33
+ export function validateGridPlacement(arch, grid, problems) {
34
+ if (!grid) return;
35
+ if (arch.layout !== undefined && arch.layout.mode !== 'grid') {
36
+ problems.push('layout.mode must be "grid" when layout is set (free placement omits layout entirely).');
37
+ return;
38
+ }
39
+ const seen = new Map();
40
+ for (const c of arch.components ?? []) {
41
+ const hasPos = Array.isArray(c.pos) && c.pos.length === 2;
42
+ const hasCell = Number.isInteger(c.row) && Number.isInteger(c.col);
43
+ if (hasPos) continue; // pos wins; row/col are optional hints only
44
+ if (!hasPos && !hasCell) {
45
+ problems.push(`Component "${c.id}" needs pos [x,y] or grid row/col when layout.mode is "grid".`);
46
+ continue;
47
+ }
48
+ if (c.row < 0 || c.col < 0) {
49
+ problems.push(`Component "${c.id}" row/col must be non-negative integers.`);
50
+ continue;
51
+ }
52
+ if (c.col >= grid.cols) {
53
+ problems.push(`Component "${c.id}" col ${c.col} exceeds layout.cols ${grid.cols} (valid: 0..${grid.cols - 1}).`);
54
+ }
55
+ const key = `${c.row},${c.col}`;
56
+ if (seen.has(key)) {
57
+ problems.push(`Components "${seen.get(key)}" and "${c.id}" share grid cell row ${c.row} col ${c.col}.`);
58
+ } else {
59
+ seen.set(key, c.id);
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,371 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { esc, renderDefinitions, textUnits } from '../shared/utils.mjs';
4
+ import { animateAttr, loadDiagram, writeDiagram, svgRootAttrs } from '../shared/cli.mjs';
5
+ import { componentBox, boundaryBox, connectionPath } from '../shared/layout-report.mjs';
6
+ import { gridLayout, resolveComponentPos, validateGridPlacement } from './grid.mjs';
7
+ import {
8
+ asArray,
9
+ isFinitePoint,
10
+ rectsOverlap,
11
+ suggestLabelObstacleFix,
12
+ suggestComponentSeparation,
13
+ anchor,
14
+ defaultFromSide,
15
+ defaultToSide,
16
+ chosenSide,
17
+ polylinePath,
18
+ roundedPath,
19
+ labelPoint,
20
+ componentFill,
21
+ componentText,
22
+ arrowClassMap,
23
+ variantAccent,
24
+ } from '../shared/geometry.mjs';
25
+
26
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
27
+ const layoutJsonMode = process.argv.includes('--layout-json');
28
+ const cliArgs = process.argv.filter((arg) => arg !== '--layout-json');
29
+ const { diagram: arch, template, outPath } = loadDiagram({
30
+ rendererDir: __dirname,
31
+ diagramType: 'architecture',
32
+ defaultExample: 'web-app.architecture.json',
33
+ argv: cliArgs,
34
+ });
35
+
36
+ const grid = gridLayout(arch);
37
+
38
+ const layout = {
39
+ defaultW: 120,
40
+ defaultH: 60,
41
+ margin: 40,
42
+ // Boundary padding - the 30/50 rule that was a hand-arithmetic footgun
43
+ // (CHANGELOG v2.2.1): 30px on top/left/right, plus 20px extra at the bottom.
44
+ boundaryPad: 30,
45
+ boundaryExtraBottom: 20,
46
+ legendH: 28,
47
+ };
48
+
49
+ // ---- Measure components from free coordinates --------------------------------
50
+ function measureComponent(c) {
51
+ const [x, y] = resolveComponentPos(c, grid);
52
+ const [w, h] = Array.isArray(c.size) ? c.size : [layout.defaultW, layout.defaultH];
53
+ return { ...c, x, y, width: w, height: h, cx: x + w / 2, cy: y + h / 2 };
54
+ }
55
+
56
+ const components = new Map(asArray(arch.components).map((c) => [c.id, measureComponent(c)]));
57
+ const componentSteps = new Map();
58
+ for (const [index, conn] of asArray(arch.connections).entries()) {
59
+ if (!componentSteps.has(conn.from)) componentSteps.set(conn.from, index);
60
+ if (!componentSteps.has(conn.to)) componentSteps.set(conn.to, index + 1);
61
+ }
62
+ for (const [index, c] of asArray(arch.components).entries()) {
63
+ if (!componentSteps.has(c.id)) componentSteps.set(c.id, index);
64
+ }
65
+
66
+ // ---- Boundaries computed from the `wraps` id list ---------------------------
67
+ function boundaryRect(boundary) {
68
+ const members = asArray(boundary.wraps).map((id) => components.get(id)).filter(Boolean);
69
+ if (!members.length) return null;
70
+ const minX = Math.min(...members.map((m) => m.x));
71
+ const minY = Math.min(...members.map((m) => m.y));
72
+ const maxX = Math.max(...members.map((m) => m.x + m.width));
73
+ const maxY = Math.max(...members.map((m) => m.y + m.height));
74
+ const pad = boundary.pad ?? layout.boundaryPad;
75
+ return {
76
+ ...boundary,
77
+ x: minX - pad,
78
+ y: minY - pad,
79
+ width: maxX - minX + pad * 2,
80
+ height: maxY - minY + pad + layout.boundaryExtraBottom,
81
+ };
82
+ }
83
+
84
+ const boundaries = asArray(arch.boundaries).map(boundaryRect).filter(Boolean);
85
+
86
+ // ---- Auto viewBox: fit all geometry + a legend row --------------------------
87
+ function autoViewBox() {
88
+ let maxX = 0;
89
+ let maxY = 0;
90
+ for (const c of components.values()) {
91
+ maxX = Math.max(maxX, c.x + c.width);
92
+ maxY = Math.max(maxY, c.y + c.height);
93
+ }
94
+ for (const b of boundaries) {
95
+ maxX = Math.max(maxX, b.x + b.width);
96
+ maxY = Math.max(maxY, b.y + b.height);
97
+ }
98
+ return [
99
+ Math.ceil(maxX + layout.margin),
100
+ Math.ceil(maxY + layout.margin + layout.legendH),
101
+ ];
102
+ }
103
+
104
+ const viewBox = arch.meta?.viewBox || autoViewBox();
105
+ const legendY = () => viewBox[1] - 16;
106
+
107
+ // ---- Validation: mechanical correctness, never layout taste -----------------
108
+ function validateArchitecture() {
109
+ const problems = [];
110
+ if (arch.schema_version !== 1) problems.push('Architecture files must set "schema_version": 1.');
111
+ if (arch.diagram_type !== 'architecture') problems.push('Architecture files must set "diagram_type": "architecture".');
112
+ if (!arch.meta?.title) problems.push('Architecture files must include meta.title.');
113
+ if (!Array.isArray(arch.components) || arch.components.length < 1) {
114
+ problems.push('Architecture diagrams need at least one component.');
115
+ }
116
+ if (arch.connections !== undefined && !Array.isArray(arch.connections)) problems.push('Architecture "connections" must be an array.');
117
+ if (arch.boundaries !== undefined && !Array.isArray(arch.boundaries)) problems.push('Architecture "boundaries" must be an array.');
118
+ if (arch.cards !== undefined && !Array.isArray(arch.cards)) problems.push('Architecture "cards" must be an array.');
119
+ if (components.size !== asArray(arch.components).length) problems.push('Component ids must be unique.');
120
+ if (grid) {
121
+ validateGridPlacement(arch, grid, problems);
122
+ } else {
123
+ for (const c of asArray(arch.components)) {
124
+ if (!Array.isArray(c.pos) || c.pos.length !== 2) {
125
+ problems.push(`Component "${c.id}" must include pos [x, y] when layout.mode is omitted (free placement).`);
126
+ }
127
+ }
128
+ }
129
+
130
+ for (const c of components.values()) {
131
+ if (!isFinitePoint(c.x, c.y, c.width, c.height)) {
132
+ problems.push(`Component "${c.id}" has non-finite pos/size - pos and size must be [number, number].`);
133
+ continue;
134
+ }
135
+ if (c.width <= 0 || c.height <= 0) {
136
+ problems.push(`Component "${c.id}" has invalid size ${c.width}x${c.height} - width and height must be greater than 0.`);
137
+ continue;
138
+ }
139
+ if (c.x < 0 || c.y < 0 || c.x + c.width > viewBox[0] || c.y + c.height > viewBox[1]) {
140
+ problems.push(`Component "${c.id}" falls outside the viewBox ${viewBox[0]}x${viewBox[1]} - adjust pos/size or set a larger meta.viewBox.`);
141
+ }
142
+ const estLabelW = textUnits(c.label) * 6.6;
143
+ if (estLabelW > c.width + 8) {
144
+ problems.push(`Label "${c.label}" (~${Math.round(estLabelW)}px) is wider than component "${c.id}" (${c.width}px) - shorten the label, move detail to sublabel, or widen size.`);
145
+ }
146
+ }
147
+
148
+ // Component overlap - the highest-traffic hand-placement failure mode.
149
+ const list = [...components.values()];
150
+ for (let i = 0; i < list.length; i += 1) {
151
+ for (let j = i + 1; j < list.length; j += 1) {
152
+ if (rectsOverlap(list[i], list[j], 8)) {
153
+ problems.push(`Components "${list[i].id}" and "${list[j].id}" are less than 8px apart - move one or shrink its size.\n${suggestComponentSeparation(list[i], list[j], 8)}`);
154
+ }
155
+ }
156
+ }
157
+
158
+ // Boundaries: every wrapped id must exist; the computed box must stay in view.
159
+ for (const boundary of asArray(arch.boundaries)) {
160
+ for (const id of asArray(boundary.wraps)) {
161
+ if (!components.has(id)) problems.push(`Boundary "${boundary.label}" wraps unknown component "${id}".`);
162
+ }
163
+ }
164
+ for (const b of boundaries) {
165
+ if (b.x < 0 || b.y < 0 || b.x + b.width > viewBox[0] || b.y + b.height > viewBox[1]) {
166
+ problems.push(`Boundary "${b.label}" extends outside the viewBox - its members sit too close to the canvas edge; add margin or enlarge meta.viewBox.`);
167
+ }
168
+ }
169
+
170
+ for (const conn of asArray(arch.connections)) {
171
+ if (!components.has(conn.from)) problems.push(`Connection "${conn.label || conn.from}" references unknown source "${conn.from}".`);
172
+ if (!components.has(conn.to)) problems.push(`Connection "${conn.label || conn.to}" references unknown target "${conn.to}".`);
173
+ if (components.has(conn.from) && components.has(conn.to)) {
174
+ const routed = pathFor(conn);
175
+ const [start, end] = [routed.points[0], routed.points[routed.points.length - 1]];
176
+ const distance = Math.hypot(end[0] - start[0], end[1] - start[1]);
177
+ if (distance < 24) problems.push(`Connection "${conn.label || `${conn.from}->${conn.to}`}" is too short (${Math.round(distance)}px; minimum 24px) - place its components farther apart.`);
178
+ }
179
+ }
180
+
181
+ // Connection labels must not land on top of components.
182
+ const labelRects = [];
183
+ for (const conn of asArray(arch.connections)) {
184
+ if (!conn.label || !components.has(conn.from) || !components.has(conn.to)) continue;
185
+ const [lx, ly] = labelPoint(conn, pathFor(conn).points);
186
+ const w = Math.max(30, textUnits(conn.label) * 4.8 + 10);
187
+ labelRects.push({ label: conn.label, x: lx - w / 2, y: ly - 10, width: w, height: 14, lx, ly });
188
+ }
189
+ for (const rect of labelRects) {
190
+ for (const c of components.values()) {
191
+ if (rectsOverlap(rect, c, -2)) {
192
+ problems.push(`Label "${rect.label}" overlaps component "${c.id}" - adjust labelDx/labelDy/labelSegment or set labelAt.\n${suggestLabelObstacleFix(rect, rect.lx, rect.ly, c)}`);
193
+ }
194
+ }
195
+ }
196
+
197
+ if (problems.length) {
198
+ throw new Error(`Architecture layout validation failed:\n- ${problems.join('\n- ')}`);
199
+ }
200
+ }
201
+
202
+ function buildLayoutReport() {
203
+ const labels = [];
204
+ for (const conn of asArray(arch.connections)) {
205
+ if (!conn.label || !components.has(conn.from) || !components.has(conn.to)) continue;
206
+ const [lx, ly] = labelPoint(conn, pathFor(conn).points);
207
+ const w = Math.max(30, textUnits(conn.label) * 4.8 + 10);
208
+ labels.push({
209
+ text: conn.label,
210
+ x: Math.round(lx - w / 2),
211
+ y: Math.round(ly - 10),
212
+ width: Math.round(w),
213
+ height: 14,
214
+ labelAt: [Math.round(lx), Math.round(ly)],
215
+ });
216
+ }
217
+ return {
218
+ ok: true,
219
+ diagram_type: 'architecture',
220
+ layout: grid ? { mode: 'grid', ...grid } : { mode: 'free' },
221
+ viewBox,
222
+ components: [...components.values()].map(componentBox),
223
+ boundaries: boundaries.map(boundaryBox),
224
+ connections: asArray(arch.connections)
225
+ .filter((conn) => components.has(conn.from) && components.has(conn.to))
226
+ .map((conn) => {
227
+ const routed = pathFor(conn);
228
+ const labelAt = conn.label ? labelPoint(conn, routed.points) : null;
229
+ return connectionPath(conn, routed, labelAt);
230
+ }),
231
+ labels,
232
+ };
233
+ }
234
+
235
+ // ---- Connection routing ------------------------------------------------------
236
+ function routeVia(conn, from, to, start, end) {
237
+ if (conn.via) return conn.via;
238
+ switch (conn.route || 'auto') {
239
+ case 'straight':
240
+ return [];
241
+ case 'orthogonal-h': {
242
+ const midX = (start[0] + end[0]) / 2;
243
+ return [[midX, start[1]], [midX, end[1]]];
244
+ }
245
+ case 'orthogonal-v': {
246
+ const midY = (start[1] + end[1]) / 2;
247
+ return [[start[0], midY], [end[0], midY]];
248
+ }
249
+ case 'auto':
250
+ default: {
251
+ // Direct line unless the anchors are clearly orthogonal-friendly.
252
+ if (Math.abs(start[0] - end[0]) < 4 || Math.abs(start[1] - end[1]) < 4) return [];
253
+ const midX = (start[0] + end[0]) / 2;
254
+ return [[midX, start[1]], [midX, end[1]]];
255
+ }
256
+ }
257
+ }
258
+
259
+ const pathCache = new Map();
260
+ function pathFor(conn) {
261
+ if (pathCache.has(conn)) return pathCache.get(conn);
262
+ const from = components.get(conn.from);
263
+ const to = components.get(conn.to);
264
+ const start = anchor(from, chosenSide(conn.fromSide, defaultFromSide(from, to)));
265
+ const end = anchor(to, chosenSide(conn.toSide, defaultToSide(from, to)));
266
+ const points = [start, ...routeVia(conn, from, to, start, end), end];
267
+ const routed = { d: roundedPath(points, 8), points };
268
+ pathCache.set(conn, routed);
269
+ return routed;
270
+ }
271
+
272
+ // ---- Rendering ---------------------------------------------------------------
273
+ function renderBoundary(b) {
274
+ const cls = b.kind === 'security-group' ? 'c-security-group' : 'c-region';
275
+ const labelCls = b.kind === 'security-group' ? 't-security' : 't-cloud';
276
+ const rx = b.kind === 'security-group' ? 8 : 12;
277
+ return ` <rect x="${b.x}" y="${b.y}" width="${b.width}" height="${b.height}" rx="${rx}" class="${cls}" stroke-width="1"/>
278
+ <text x="${b.x + 8}" y="${b.y + 18}" class="${labelCls}" font-size="9" font-weight="600">${esc(b.label)}</text>`;
279
+ }
280
+
281
+ function renderConnectionPath(conn, index) {
282
+ const [cls, marker] = arrowClassMap[conn.variant || 'default'] || arrowClassMap.default;
283
+ const routed = pathFor(conn);
284
+ const strokeWidth = conn.width || (conn.variant === 'emphasis' ? 1.8 : 1.5);
285
+ return ` <path d="${routed.d}" class="${cls}"${animateAttr(arch.meta, 'edge', index)} stroke-width="${strokeWidth}" marker-end="url(#${marker})"/>`;
286
+ }
287
+
288
+ function renderConnectionLabel(conn) {
289
+ if (!conn.label) return '';
290
+ const [lx, ly] = labelPoint(conn, pathFor(conn).points);
291
+ const w = Math.max(30, textUnits(conn.label) * 4.8 + 10);
292
+ return ` <rect x="${lx - w / 2}" y="${ly - 10}" width="${w}" height="14" rx="3" class="c-mask"/>
293
+ <text x="${lx}" y="${ly}" class="${variantAccent(conn.variant)}" font-size="8" text-anchor="middle">${esc(conn.label)}</text>`;
294
+ }
295
+
296
+ function renderComponent(c) {
297
+ const fill = componentFill[c.type] || 'c-external';
298
+ const accent = componentText[c.type] || 't-muted';
299
+ const cx = c.cx;
300
+ const hasSub = c.sublabel != null && c.sublabel !== '';
301
+ const labelY = hasSub ? c.y + c.height / 2 - 2 : c.y + c.height / 2 + 4;
302
+ const sub = hasSub
303
+ ? `\n <text x="${cx}" y="${c.y + c.height / 2 + 14}" class="t-muted" font-size="9" text-anchor="middle">${esc(c.sublabel)}</text>`
304
+ : '';
305
+ const tag = c.tag
306
+ ? `\n <text x="${cx}" y="${c.y + c.height - 8}" class="${accent}" font-size="7" text-anchor="middle">${esc(c.tag)}</text>`
307
+ : '';
308
+ return ` <rect x="${c.x}" y="${c.y}" width="${c.width}" height="${c.height}" rx="6" class="c-mask"/>
309
+ <rect x="${c.x}" y="${c.y}" width="${c.width}" height="${c.height}" rx="6" class="${fill}"${animateAttr(arch.meta, 'node', componentSteps.get(c.id))} stroke-width="1.5"/>
310
+ <text x="${cx}" y="${labelY}" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">${esc(c.label)}</text>${sub}${tag}`;
311
+ }
312
+
313
+ // Auto legend: one swatch per component type actually used, left to right.
314
+ const TYPE_LABELS = {
315
+ frontend: 'Frontend', backend: 'Backend', database: 'Database', cloud: 'Cloud',
316
+ security: 'Security', messagebus: 'Message bus', external: 'External',
317
+ };
318
+ function renderLegend() {
319
+ const used = [];
320
+ const seen = new Set();
321
+ for (const c of components.values()) {
322
+ if (!seen.has(c.type)) { seen.add(c.type); used.push(c.type); }
323
+ }
324
+ const y = legendY();
325
+ let x = layout.margin;
326
+ const parts = [` <text x="${x}" y="${y - 13}" class="t-primary" font-size="9" font-weight="600">Legend</text>`];
327
+ for (const type of used) {
328
+ parts.push(` <rect x="${x}" y="${y - 8}" width="14" height="9" rx="2" class="${componentFill[type] || 'c-external'}" stroke-width="1"/>`);
329
+ parts.push(` <text x="${x + 20}" y="${y}" class="t-muted" font-size="8">${TYPE_LABELS[type] || type}</text>`);
330
+ x += 30 + (textUnits(TYPE_LABELS[type] || type) * 5 + 28);
331
+ }
332
+ return parts.join('\n');
333
+ }
334
+
335
+ function renderSvg() {
336
+ return ` <svg viewBox="0 0 ${viewBox[0]} ${viewBox[1]}" ${svgRootAttrs(arch.meta, 'architecture diagram')}>
337
+ ${renderDefinitions()}
338
+
339
+ <!-- Background Grid -->
340
+ <rect width="100%" height="100%" fill="url(#grid)" />
341
+
342
+ <!-- Boundaries (behind everything) -->
343
+ ${boundaries.map(renderBoundary).join('\n\n')}
344
+
345
+ <!-- Connection paths (before components for correct z-order) -->
346
+ ${asArray(arch.connections).map(renderConnectionPath).join('\n')}
347
+
348
+ <!-- Components -->
349
+ ${[...components.values()].map(renderComponent).join('\n\n')}
350
+
351
+ <!-- Connection labels -->
352
+ ${asArray(arch.connections).map(renderConnectionLabel).join('\n')}
353
+
354
+ <!-- Legend -->
355
+ ${renderLegend()}
356
+ </svg>`;
357
+ }
358
+
359
+ validateArchitecture();
360
+ if (layoutJsonMode) {
361
+ console.log(JSON.stringify(buildLayoutReport(), null, 2));
362
+ process.exit(0);
363
+ }
364
+ writeDiagram({
365
+ outPath,
366
+ template,
367
+ meta: arch.meta,
368
+ footerLabel: 'Architecture diagram',
369
+ svg: renderSvg(),
370
+ cards: arch.cards,
371
+ });
@@ -0,0 +1,82 @@
1
+ # Data Flow Renderer
2
+
3
+ Render `diagram_type: "dataflow"` JSON files into the standalone technical diagram HTML
4
+ template.
5
+
6
+ ```bash
7
+ bun --config=.claude/skills/stacks-technical-diagrams/bunfig.toml --no-env-file .claude/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs input.dataflow.json output.html
8
+ ```
9
+
10
+ The renderer validates input against `.claude/skills/stacks-technical-diagrams/schemas/dataflow.schema.json`
11
+ with the bundled standalone validator. No dependency installation is required.
12
+
13
+ If `output.html` is omitted, the renderer uses `meta.output` from the JSON file
14
+ or falls back to `dataflow.html` in the current working directory.
15
+
16
+ ## Input
17
+
18
+ Data-flow JSON files must set:
19
+
20
+ ```json
21
+ {
22
+ "schema_version": 1,
23
+ "diagram_type": "dataflow",
24
+ "meta": {
25
+ "title": "Product Analytics Data Flow",
26
+ "subtitle": "Events, consent, PII isolation, warehouse sync, and consumers",
27
+ "viewBox": [940, 720]
28
+ },
29
+ "stages": [],
30
+ "nodes": [],
31
+ "flows": [],
32
+ "cards": []
33
+ }
34
+ ```
35
+
36
+ A complete worked example lives at
37
+ `.claude/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json`.
38
+
39
+ The schema lives at:
40
+
41
+ ```text
42
+ .claude/skills/stacks-technical-diagrams/schemas/dataflow.schema.json
43
+ ```
44
+
45
+ ## Layout budget
46
+
47
+ | Constant | Value |
48
+ |----------|-------|
49
+ | viewBox | default `[940, 720]`; schema minimum `[360, 360]` |
50
+ | Stages (2-5) | centers at x = 100 + stage×215; stage band 168 wide, header at y 46 |
51
+ | Row tops (`row` 0-4) | y = 128, 242, 356, 470, 584 (plus `yOffset`) |
52
+ | Default node | 112×58 |
53
+ | Node area | x within `[24, width − 24]`; y within `[104, height − 74]` |
54
+ | Node spacing | ≥10px between any two nodes (checked across stages and rows) |
55
+ | Flow length | ≥34px between endpoints |
56
+ | Legend row | y = height − 36 |
57
+
58
+ Route presets for flows: `straight`, `vertical-channel`, `bottom-channel`,
59
+ `top-channel`, explicit `via` points, or the default `auto` (midpoint elbow).
60
+
61
+ ## Design Rules
62
+
63
+ - Use stages for data lifecycle boundaries: source, ingest, process, store,
64
+ consume.
65
+ - Place nodes by stage index and row index; do not hand-place raw SVG for the
66
+ common case.
67
+ - Use flow labels to name the data asset, not the transport primitive:
68
+ `clickstream`, `identity map`, `normalized facts`, `feature vectors`.
69
+ - Use `classification` for short sensitivity or governance context:
70
+ `PII touch`, `non-PII`, `approved only`, `batch`, `read-only`.
71
+ - Use `security` for PII, policy, consent, access-control, or restricted joins.
72
+ - Use `emphasis` for the primary data path and `dashed` for async or batch
73
+ derivations.
74
+ - Keep labels short enough to fit in narrow previews.
75
+
76
+ Schema violations exit non-zero with path-prefixed messages annotated with the
77
+ element's id or label. The renderer additionally fails when it can detect
78
+ layout problems, including missing stages, duplicate node IDs, nodes outside
79
+ the readable diagram area, node overlap, labels colliding with nodes or other
80
+ labels, labels wider than their node, unknown flow endpoints, missing flow
81
+ labels, unreadably short flows, or stages that exceed the viewBox. Text width
82
+ is estimated CJK-aware: fullwidth glyphs count as two units.