@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,452 @@
1
+
2
+ @title('Dashboard - Cloud')
3
+ <div class="py-4 lg:py-8 min-h-screen dark:bg-neutral-800">
4
+ <div class="px-4 sm:px-6 lg:px-8">
5
+ <PageHeader title="Cloud Infrastructure" description="Design and manage your cloud infrastructure." />
6
+
7
+ <dl class="grid gap-5 grid-cols-2 sm:grid-cols-4 mt-8">
8
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-700 rounded-lg shadow">
9
+ <dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Provider</dt>
10
+ <dd class="mt-1 font-semibold text-2xl text-gray-900 uppercase dark:text-white">{{ cloudConfig.type }}</dd>
11
+ </div>
12
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-700 rounded-lg shadow">
13
+ <dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Servers</dt>
14
+ <dd class="mt-1 font-semibold text-2xl text-gray-900 dark:text-white">{{ serversWithDetails.length }}</dd>
15
+ </div>
16
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-700 rounded-lg shadow">
17
+ <dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Server Types</dt>
18
+ <dd class="mt-1 font-semibold text-2xl text-gray-900 dark:text-white">{{ serversByType.length }}</dd>
19
+ </div>
20
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-700 rounded-lg shadow">
21
+ <dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Load Balancer</dt>
22
+ <dd class="mt-1 font-semibold text-2xl text-gray-900 dark:text-white">{{ cloudConfig.useLoadBalancer ? 'Enabled' : 'Disabled' }}</dd>
23
+ </div>
24
+ </dl>
25
+
26
+ <div class="mt-8 bg-white dark:bg-neutral-700 rounded-lg shadow">
27
+ <div class="p-6">
28
+ <h4 class="font-medium text-base text-gray-900 dark:text-neutral-100">Topology breakdown</h4>
29
+ <div class="grid gap-3 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 mt-4">
30
+ <template :for="group in serversByType">
31
+ <div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-neutral-800 rounded-lg">
32
+ <span :class="typeBadgeClass(group.type)">{{ group.label }}</span>
33
+ <span class="font-mono text-gray-900 text-sm dark:text-white">×{{ group.count }}</span>
34
+ </div>
35
+ </template>
36
+ </div>
37
+
38
+ <div class="flex items-center justify-between mb-3 mt-6">
39
+ <h5 class="font-medium text-gray-900 text-sm dark:text-neutral-100">Diagram</h5>
40
+ <p class="text-gray-500 text-xs dark:text-neutral-400">Drag nodes · scroll to zoom</p>
41
+ </div>
42
+ <div id="cloud_topology" class="h-[420px] w-full bg-neutral-50 dark:bg-neutral-800 rounded-lg"></div>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="mt-8">
47
+ <div class="flex items-center justify-between mb-6">
48
+ <div>
49
+ <h4 class="font-medium text-base text-gray-900 dark:text-neutral-100">Servers</h4>
50
+ <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Configured application servers and their specifications.</p>
51
+ </div>
52
+ <div class="flex gap-4 items-center">
53
+ <StxLink to="/servers" class="inline-flex gap-1 items-center text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">
54
+ View All
55
+ <div class="h-4 w-4 i-hugeicons-arrow-right-01"></div>
56
+ </StxLink>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="space-y-4">
61
+ <template :for="server in serversWithDetails">
62
+ <div @click="selectServer(server.key)" class="p-6 bg-white dark:bg-neutral-700 border border-transparent rounded-lg dark:border-neutral-600 hover:border-blue-300 dark:hover:border-blue-500 shadow cursor-pointer">
63
+ <div class="flex flex-wrap gap-4 items-start justify-between">
64
+ <div class="flex-1 min-w-0">
65
+ <div class="flex flex-wrap gap-3 items-center">
66
+ <h5 class="font-medium text-gray-900 text-lg dark:text-neutral-100">{{ server.name }}</h5>
67
+ <span :class="typeBadgeClass(server.type)">{{ server.label }}</span>
68
+ </div>
69
+ <div class="flex gap-2 items-center mt-1 text-gray-500 text-sm dark:text-neutral-400">
70
+ <div class="h-4 w-4 i-hugeicons-link"></div>
71
+ <p>{{ server.domain }}</p>
72
+ </div>
73
+ <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">{{ server.description }}</p>
74
+ <div class="flex flex-wrap gap-2 mt-3">
75
+ <template :for="badge in server.badges">
76
+ <span class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs dark:text-blue-300 bg-blue-100 dark:bg-blue-900 rounded-md">{{ badge }}</span>
77
+ </template>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="flex flex-col gap-1 items-end text-right">
82
+ <span class="text-gray-500 text-xs dark:text-neutral-400">{{ server.region }}</span>
83
+ <span class="font-mono text-gray-900 text-sm dark:text-white">{{ server.size }}</span>
84
+ <span class="text-gray-500 text-xs dark:text-neutral-400">{{ server.ramGb }}GB RAM · {{ server.specs.vcpu }} vCPU · {{ server.arch }}</span>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </template>
89
+ </div>
90
+ </div>
91
+
92
+ <div :show="selectedServer() !== null" @click.self="closeServer()" class="flex fixed inset-0 z-50 items-center justify-center p-4 bg-black bg-opacity-50">
93
+ <div class="overflow-y-auto p-6 max-h-[90vh] max-w-2xl w-full bg-white dark:bg-neutral-800 rounded-lg shadow-xl">
94
+ <div class="flex items-start justify-between">
95
+ <div>
96
+ <h3 class="font-semibold text-gray-900 text-lg dark:text-white">{{ selectedServer()?.name }}</h3>
97
+ <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">{{ selectedServer()?.domain }}</p>
98
+ </div>
99
+ <button @click="closeServer()" class="text-gray-400 dark:text-neutral-300 hover:text-gray-600 dark:hover:text-white">
100
+ <i class="h-5 w-5 i-hugeicons-cancel-01"></i>
101
+ </button>
102
+ </div>
103
+
104
+ <dl class="grid gap-3 grid-cols-1 sm:grid-cols-2 mt-6">
105
+ <template :for="entry in selectedDetails">
106
+ <div class="flex gap-4 justify-between py-2 border-gray-100 border-t dark:border-neutral-700">
107
+ <dt class="text-gray-500 text-sm dark:text-neutral-400">{{ entry.label }}</dt>
108
+ <dd class="font-medium text-gray-900 text-right text-sm dark:text-white">{{ entry.value }}</dd>
109
+ </div>
110
+ </template>
111
+ </dl>
112
+ </div>
113
+ </div>
114
+
115
+ <div class="mt-12">
116
+ <div class="flex items-center justify-between mb-4">
117
+ <div>
118
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-neutral-100">Recent Events</h3>
119
+ <p class="mt-2 text-gray-700 text-sm dark:text-neutral-400">Latest server events and notifications.</p>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
124
+ <table class="min-w-full divide-gray-300 divide-y dark:divide-neutral-600">
125
+ <thead class="bg-gray-50 dark:bg-neutral-600">
126
+ <tr>
127
+ <th scope="col" class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm dark:text-neutral-100">Type</th>
128
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-100">Service</th>
129
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-100">Message</th>
130
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm dark:text-neutral-100">Timestamp</th>
131
+ </tr>
132
+ </thead>
133
+ <tbody class="bg-white dark:bg-neutral-700 divide-gray-200 divide-y dark:divide-neutral-700">
134
+ <template :for="event in recentEvents">
135
+ <tr class="hover:bg-gray-50 dark:hover:bg-neutral-600">
136
+ <td class="pl-4 pr-3 py-4 sm:pl-6 text-sm whitespace-nowrap">
137
+ <div :class="eventIcon(event.type) + ' ' + eventColor(event.type)" class="h-5 w-5"></div>
138
+ </td>
139
+ <td class="px-3 py-4 text-gray-900 text-sm whitespace-nowrap dark:text-neutral-100">{{ event.service }}</td>
140
+ <td class="px-3 py-4 text-gray-500 text-sm dark:text-neutral-400">{{ event.message }}</td>
141
+ <td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-400">{{ event.timestamp }}</td>
142
+ </tr>
143
+ </template>
144
+ </tbody>
145
+ </table>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </div>
150
+
151
+ <script client>
152
+ // Topology demo data + view helpers — moved out of the former
153
+ // `<script server>` block (stacksjs/stacks#1838).
154
+ const cloudConfig = {
155
+ type: 'aws',
156
+ useLoadBalancer: true,
157
+ }
158
+
159
+ const workerSizes: Record<string, { vcpu: number, ram: number }> = {
160
+ 't2.micro': { vcpu: 1, ram: 1024 },
161
+ 't2.small': { vcpu: 1, ram: 2048 },
162
+ 't2.medium': { vcpu: 2, ram: 4096 },
163
+ 't2.large': { vcpu: 2, ram: 8192 },
164
+ 't3.micro': { vcpu: 2, ram: 1024 },
165
+ 't3.small': { vcpu: 2, ram: 2048 },
166
+ 't3.medium': { vcpu: 2, ram: 4096 },
167
+ 't3.large': { vcpu: 2, ram: 8192 },
168
+ 'm5.large': { vcpu: 2, ram: 8192 },
169
+ 'm5.xlarge': { vcpu: 4, ram: 16384 },
170
+ 'c5.large': { vcpu: 2, ram: 4096 },
171
+ 't4g.micro': { vcpu: 2, ram: 1024 },
172
+ 't4g.medium': { vcpu: 2, ram: 4096 },
173
+ }
174
+
175
+ const serverTypeMeta: Record<string, { label: string, badges: string[], description: string }> = {
176
+ app: { label: 'Application Server', badges: ['Bun', 'Database', 'Nginx', 'Redis', 'Memcached', 'Search Engine'], description: 'These servers are meant to be networked to dedicated cache or database servers.' },
177
+ web: { label: 'Web Server', badges: ['Bun', 'Nginx'], description: 'A secure Nginx web server.' },
178
+ worker: { label: 'Worker Server', badges: ['Bun'], description: 'A dedicated worker server for processing queued jobs.' },
179
+ cache: { label: 'Cache Server', badges: ['Redis', 'Memcached'], description: 'A dedicated cache server for improved performance.' },
180
+ database: { label: 'Database Server', badges: ['Database'], description: 'A dedicated database server for your services.' },
181
+ search: { label: 'Search Server', badges: ['Search Engine', 'Nginx'], description: 'A dedicated search server for your services.' },
182
+ loadbalancer: { label: 'Load Balancer', badges: ['Nginx'], description: 'A load balancer to distribute traffic between two or more servers.' },
183
+ }
184
+
185
+ const serversRaw = [
186
+ { key: 'app', name: 'app-server-1', domain: 'stacksjs.com', region: 'us-east-1', type: 'app', size: 't3.micro', diskSize: 20, privateNetwork: 'vpc-123456789', subnet: 'subnet-123456789', serverOS: 'ubuntu-20-lts-x86_64', bunVersion: '1.1.26', database: 'sqlite', databaseName: 'stacks' },
187
+ { key: 'worker', name: 'worker-server-1', domain: 'stacksjs.com', region: 'us-east-1', type: 'worker', size: 't3.small', diskSize: 20, privateNetwork: 'vpc-123456789', subnet: 'subnet-123456789', serverOS: 'ubuntu-24-lts-x86_64', bunVersion: 'v1.2.3', database: '', databaseName: '' },
188
+ { key: 'database', name: 'db-server-1', domain: 'stacksjs.com', region: 'us-east-1', type: 'database', size: 't3.medium', diskSize: 50, privateNetwork: 'vpc-123456789', subnet: 'subnet-123456789', serverOS: 'ubuntu-24-lts-x86_64', bunVersion: '', database: 'postgresql', databaseName: 'stacks_prod' },
189
+ ]
190
+
191
+ const serversWithDetails = serversRaw.map((server) => {
192
+ const specs = workerSizes[server.size] ?? { vcpu: 0, ram: 0 }
193
+ const meta = serverTypeMeta[server.type] ?? { label: server.type, badges: [], description: '' }
194
+ return {
195
+ ...server,
196
+ specs,
197
+ ramGb: (specs.ram / 1024).toFixed(0),
198
+ arch: server.size.includes('g.') ? 'ARM' : 'x86',
199
+ label: meta.label,
200
+ badges: meta.badges,
201
+ description: meta.description,
202
+ }
203
+ })
204
+
205
+ const serversByType = ['loadbalancer', 'app', 'web', 'worker', 'cache', 'database', 'search'].map(type => ({
206
+ type,
207
+ label: serverTypeMeta[type]?.label ?? type,
208
+ count: serversWithDetails.filter(s => s.type === type).length,
209
+ })).filter(group => group.count > 0)
210
+
211
+ const recentEvents = [
212
+ { id: '1', service: 'App Server', type: 'success', message: 'Server deployment completed successfully', timestamp: '2024-03-21 14:23:45' },
213
+ { id: '2', service: 'Worker', type: 'info', message: 'New worker instance launched', timestamp: '2024-03-21 14:20:12' },
214
+ { id: '3', service: 'Database', type: 'warning', message: 'High CPU utilization detected', timestamp: '2024-03-21 14:15:30' },
215
+ ]
216
+
217
+ function eventIcon(type: string): string {
218
+ switch (type) {
219
+ case 'success': return 'i-hugeicons-checkmark-circle-02'
220
+ case 'error': return 'i-hugeicons-cancel-circle'
221
+ case 'warning': return 'i-hugeicons-alert-02'
222
+ default: return 'i-hugeicons-information-circle'
223
+ }
224
+ }
225
+
226
+ function eventColor(type: string): string {
227
+ switch (type) {
228
+ case 'success': return 'text-green-500'
229
+ case 'error': return 'text-red-500'
230
+ case 'warning': return 'text-yellow-500'
231
+ default: return 'text-blue-500'
232
+ }
233
+ }
234
+
235
+ function typeBadgeClass(type: string): string {
236
+ const base = 'inline-flex px-2 py-0.5 font-semibold leading-5 text-xs rounded-full'
237
+ switch (type) {
238
+ case 'app': return `${base} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200`
239
+ case 'worker': return `${base} bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200`
240
+ case 'cache': return `${base} bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200`
241
+ case 'database': return `${base} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200`
242
+ case 'search': return `${base} bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-200`
243
+ case 'web': return `${base} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200`
244
+ case 'loadbalancer': return `${base} bg-pink-100 text-pink-800 dark:bg-pink-900 dark:text-pink-200`
245
+ default: return `${base} bg-gray-100 text-gray-800 dark:bg-neutral-700 dark:text-neutral-200`
246
+ }
247
+ }
248
+
249
+ interface ServerSummary {
250
+ key: string
251
+ name: string
252
+ domain: string
253
+ region: string
254
+ type: string
255
+ label: string
256
+ size: string
257
+ diskSize: number
258
+ serverOS: string
259
+ bunVersion: string
260
+ database: string
261
+ databaseName: string
262
+ privateNetwork?: string
263
+ subnet?: string
264
+ ramGb: string
265
+ vcpu: number
266
+ arch: string
267
+ }
268
+
269
+ const serversData: ServerSummary[] = [
270
+ { key: 'app', name: 'app-server-1', domain: 'stacksjs.com', region: 'us-east-1', type: 'app', label: 'Application Server', size: 't3.micro', diskSize: 20, serverOS: 'ubuntu-20-lts-x86_64', bunVersion: '1.1.26', database: 'sqlite', databaseName: 'stacks', privateNetwork: 'vpc-123456789', subnet: 'subnet-123456789', ramGb: '1', vcpu: 2, arch: 'x86' },
271
+ { key: 'worker', name: 'worker-server-1', domain: 'stacksjs.com', region: 'us-east-1', type: 'worker', label: 'Worker Server', size: 't3.small', diskSize: 20, serverOS: 'ubuntu-24-lts-x86_64', bunVersion: 'v1.2.3', database: '', databaseName: '', privateNetwork: 'vpc-123456789', subnet: 'subnet-123456789', ramGb: '2', vcpu: 2, arch: 'x86' },
272
+ { key: 'database', name: 'db-server-1', domain: 'stacksjs.com', region: 'us-east-1', type: 'database', label: 'Database Server', size: 't3.medium', diskSize: 50, serverOS: 'ubuntu-24-lts-x86_64', bunVersion: '', database: 'postgresql', databaseName: 'stacks_prod', privateNetwork: 'vpc-123456789', subnet: 'subnet-123456789', ramGb: '4', vcpu: 2, arch: 'x86' },
273
+ ]
274
+
275
+ const selectedServerKey = state<string | null>(null)
276
+
277
+ const selectedServer = derived(() => serversData.find(s => s.key === selectedServerKey()) ?? null)
278
+
279
+ const selectedDetails = derived(() => {
280
+ const server = selectedServer()
281
+ if (!server)
282
+ return []
283
+ const entries: Array<{ label: string, value: string }> = [
284
+ { label: 'Type', value: server.label },
285
+ { label: 'Domain', value: server.domain },
286
+ { label: 'Region', value: server.region },
287
+ { label: 'Size', value: server.size },
288
+ { label: 'CPU / RAM', value: `${server.vcpu} vCPU · ${server.ramGb}GB` },
289
+ { label: 'Architecture', value: server.arch },
290
+ { label: 'Disk size', value: `${server.diskSize}GB` },
291
+ { label: 'OS', value: server.serverOS },
292
+ ]
293
+ if (server.bunVersion)
294
+ entries.push({ label: 'Bun version', value: server.bunVersion })
295
+ if (server.database)
296
+ entries.push({ label: 'Database', value: `${server.database}${server.databaseName ? ` (${server.databaseName})` : ''}` })
297
+ if (server.privateNetwork)
298
+ entries.push({ label: 'Network', value: server.privateNetwork })
299
+ if (server.subnet)
300
+ entries.push({ label: 'Subnet', value: server.subnet })
301
+ return entries
302
+ })
303
+
304
+ function selectServer(key: string) {
305
+ selectedServerKey.set(key)
306
+ }
307
+
308
+ function closeServer() {
309
+ selectedServerKey.set(null)
310
+ }
311
+
312
+ function topoFill(type: string): { ring: string, fill: string } {
313
+ switch (type) {
314
+ case 'dns': return { ring: '#EC4899', fill: '#FCE7F3' }
315
+ case 'lb': return { ring: '#3B82F6', fill: '#DBEAFE' }
316
+ case 'app': return { ring: '#10B981', fill: '#D1FAE5' }
317
+ case 'web': return { ring: '#EF4444', fill: '#FEE2E2' }
318
+ case 'worker': return { ring: '#F59E0B', fill: '#FEF3C7' }
319
+ case 'cache': return { ring: '#A855F7', fill: '#F3E8FF' }
320
+ case 'database': return { ring: '#22C55E', fill: '#DCFCE7' }
321
+ case 'search': return { ring: '#6366F1', fill: '#E0E7FF' }
322
+ default: return { ring: '#6B7280', fill: '#F3F4F6' }
323
+ }
324
+ }
325
+
326
+ let topologyCleanup: (() => void) | null = null
327
+
328
+ interface TopoNode { id: string, type: string, label: string, key?: string, x?: number, y?: number, fx?: number | null, fy?: number | null }
329
+ interface TopoLink { source: string | TopoNode, target: string | TopoNode }
330
+
331
+ async function renderTopology() {
332
+ const container = document.getElementById('cloud_topology')
333
+ if (!container)
334
+ return
335
+ topologyCleanup?.()
336
+ container.innerHTML = ''
337
+
338
+ const d3 = await import('d3')
339
+ const width = container.clientWidth || 800
340
+ const height = container.clientHeight || 420
341
+
342
+ const useLb = true
343
+ const nodes: TopoNode[] = [
344
+ { id: 'dns', type: 'dns', label: 'DNS' },
345
+ ...(useLb ? [{ id: 'lb', type: 'lb', label: 'Load balancer' } as TopoNode] : []),
346
+ ...serversData.map(s => ({ id: s.key, type: s.type, label: s.name, key: s.key })),
347
+ ]
348
+ const links: TopoLink[] = []
349
+ if (useLb) {
350
+ links.push({ source: 'dns', target: 'lb' })
351
+ for (const s of serversData) links.push({ source: 'lb', target: s.key })
352
+ }
353
+ else {
354
+ for (const s of serversData) links.push({ source: 'dns', target: s.key })
355
+ }
356
+
357
+ const svg = d3.select(container)
358
+ .append('svg')
359
+ .attr('width', width)
360
+ .attr('height', height)
361
+ .attr('viewBox', [0, 0, width, height])
362
+ .attr('style', 'max-width: 100%; height: auto;')
363
+
364
+ const zoomGroup = svg.append('g')
365
+ const zoom = d3.zoom<SVGSVGElement, unknown>()
366
+ .scaleExtent([0.3, 3])
367
+ .on('zoom', event => zoomGroup.attr('transform', event.transform.toString()))
368
+ svg.call(zoom as any)
369
+
370
+ const link = zoomGroup.append('g')
371
+ .attr('stroke', '#94A3B8')
372
+ .attr('stroke-opacity', 0.5)
373
+ .attr('stroke-dasharray', '4,4')
374
+ .selectAll('line')
375
+ .data(links)
376
+ .join('line')
377
+ .attr('stroke-width', 2)
378
+
379
+ const node = zoomGroup.append('g')
380
+ .selectAll<SVGGElement, TopoNode>('g')
381
+ .data(nodes)
382
+ .join('g')
383
+ .attr('cursor', d => d.key ? 'pointer' : 'default')
384
+ .on('click', (_event, d) => {
385
+ if (d.key)
386
+ selectServer(d.key)
387
+ })
388
+
389
+ node.append('circle')
390
+ .attr('r', 26)
391
+ .attr('fill', d => topoFill(d.type).fill)
392
+ .attr('stroke', d => topoFill(d.type).ring)
393
+ .attr('stroke-width', 3)
394
+
395
+ node.append('text')
396
+ .text(d => d.label.length > 14 ? `${d.label.slice(0, 12)}…` : d.label)
397
+ .attr('text-anchor', 'middle')
398
+ .attr('dy', 44)
399
+ .attr('fill', 'currentColor')
400
+ .attr('font-size', '11px')
401
+ .attr('font-weight', '500')
402
+ .attr('class', 'text-gray-700 dark:text-neutral-200')
403
+ .attr('pointer-events', 'none')
404
+
405
+ const drag = d3.drag<SVGGElement, TopoNode>()
406
+ .on('start', (event, d) => {
407
+ if (!event.active)
408
+ simulation.alphaTarget(0.3).restart()
409
+ d.fx = d.x
410
+ d.fy = d.y
411
+ })
412
+ .on('drag', (event, d) => { d.fx = event.x; d.fy = event.y })
413
+ .on('end', (event, d) => {
414
+ if (!event.active)
415
+ simulation.alphaTarget(0)
416
+ d.fx = null
417
+ d.fy = null
418
+ })
419
+ node.call(drag as any)
420
+
421
+ const simulation = d3.forceSimulation<TopoNode>(nodes)
422
+ .force('link', d3.forceLink<TopoNode, TopoLink>(links).id(d => d.id).distance(120).strength(1))
423
+ .force('charge', d3.forceManyBody().strength(-380))
424
+ .force('x', d3.forceX<TopoNode>(d => d.type === 'dns' ? width * 0.15 : d.type === 'lb' ? width * 0.4 : width * 0.75).strength(0.6))
425
+ .force('y', d3.forceY<TopoNode>(height / 2).strength(0.15))
426
+ .force('collide', d3.forceCollide<TopoNode>().radius(38))
427
+ .on('tick', () => {
428
+ link
429
+ .attr('x1', d => (d.source as TopoNode).x ?? 0)
430
+ .attr('y1', d => (d.source as TopoNode).y ?? 0)
431
+ .attr('x2', d => (d.target as TopoNode).x ?? 0)
432
+ .attr('y2', d => (d.target as TopoNode).y ?? 0)
433
+ node.attr('transform', d => `translate(${d.x ?? 0},${d.y ?? 0})`)
434
+ })
435
+
436
+ topologyCleanup = () => {
437
+ simulation.stop()
438
+ container.innerHTML = ''
439
+ }
440
+ }
441
+
442
+ onMount(() => {
443
+ void renderTopology()
444
+ })
445
+
446
+ onDestroy(() => {
447
+ topologyCleanup?.()
448
+ topologyCleanup = null
449
+ })
450
+ </script>
451
+
452
+ @endsection