@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.
- package/TODO.md +21 -0
- package/ai/AGENTS.md +79 -0
- package/ai/README.md +45 -0
- package/ai/claude/launch.json +12 -0
- package/ai/skills/stacks-actions/SKILL.md +128 -0
- package/ai/skills/stacks-ai/SKILL.md +276 -0
- package/ai/skills/stacks-alias/SKILL.md +94 -0
- package/ai/skills/stacks-analytics/SKILL.md +91 -0
- package/ai/skills/stacks-api/SKILL.md +738 -0
- package/ai/skills/stacks-arrays/SKILL.md +277 -0
- package/ai/skills/stacks-auth/SKILL.md +422 -0
- package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
- package/ai/skills/stacks-brandkit/SKILL.md +811 -0
- package/ai/skills/stacks-browse/SKILL.md +127 -0
- package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
- package/ai/skills/stacks-browser/SKILL.md +145 -0
- package/ai/skills/stacks-buddy/SKILL.md +676 -0
- package/ai/skills/stacks-build/SKILL.md +163 -0
- package/ai/skills/stacks-cache/SKILL.md +125 -0
- package/ai/skills/stacks-calendar/SKILL.md +113 -0
- package/ai/skills/stacks-chat/SKILL.md +371 -0
- package/ai/skills/stacks-cli/SKILL.md +112 -0
- package/ai/skills/stacks-cloud/SKILL.md +192 -0
- package/ai/skills/stacks-cms/SKILL.md +217 -0
- package/ai/skills/stacks-collections/SKILL.md +82 -0
- package/ai/skills/stacks-commerce/SKILL.md +114 -0
- package/ai/skills/stacks-composables/SKILL.md +161 -0
- package/ai/skills/stacks-config/SKILL.md +144 -0
- package/ai/skills/stacks-configuration/SKILL.md +154 -0
- package/ai/skills/stacks-cron/SKILL.md +123 -0
- package/ai/skills/stacks-crosswind/SKILL.md +190 -0
- package/ai/skills/stacks-dashboard/SKILL.md +173 -0
- package/ai/skills/stacks-database/SKILL.md +286 -0
- package/ai/skills/stacks-datetime/SKILL.md +269 -0
- package/ai/skills/stacks-dependencies/SKILL.md +111 -0
- package/ai/skills/stacks-deploy/SKILL.md +102 -0
- package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
- package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
- package/ai/skills/stacks-design-output/SKILL.md +55 -0
- package/ai/skills/stacks-design-soft/SKILL.md +156 -0
- package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
- package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
- package/ai/skills/stacks-desktop/SKILL.md +78 -0
- package/ai/skills/stacks-development/SKILL.md +298 -0
- package/ai/skills/stacks-dns/SKILL.md +149 -0
- package/ai/skills/stacks-docs/SKILL.md +109 -0
- package/ai/skills/stacks-email/SKILL.md +236 -0
- package/ai/skills/stacks-enums/SKILL.md +100 -0
- package/ai/skills/stacks-env/SKILL.md +181 -0
- package/ai/skills/stacks-error-handling/SKILL.md +137 -0
- package/ai/skills/stacks-events/SKILL.md +228 -0
- package/ai/skills/stacks-faker/SKILL.md +158 -0
- package/ai/skills/stacks-git/SKILL.md +158 -0
- package/ai/skills/stacks-guard/SKILL.md +98 -0
- package/ai/skills/stacks-health/SKILL.md +75 -0
- package/ai/skills/stacks-http/SKILL.md +316 -0
- package/ai/skills/stacks-i18n/SKILL.md +198 -0
- package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
- package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
- package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
- package/ai/skills/stacks-investigate/SKILL.md +119 -0
- package/ai/skills/stacks-jobs/SKILL.md +136 -0
- package/ai/skills/stacks-lint/SKILL.md +184 -0
- package/ai/skills/stacks-listeners/SKILL.md +99 -0
- package/ai/skills/stacks-logging/SKILL.md +78 -0
- package/ai/skills/stacks-mail/SKILL.md +120 -0
- package/ai/skills/stacks-middleware/SKILL.md +249 -0
- package/ai/skills/stacks-migrations/SKILL.md +105 -0
- package/ai/skills/stacks-models/SKILL.md +267 -0
- package/ai/skills/stacks-new-feature/SKILL.md +206 -0
- package/ai/skills/stacks-notifications/SKILL.md +210 -0
- package/ai/skills/stacks-objects/SKILL.md +205 -0
- package/ai/skills/stacks-office-hours/SKILL.md +134 -0
- package/ai/skills/stacks-orm/SKILL.md +342 -0
- package/ai/skills/stacks-path/SKILL.md +118 -0
- package/ai/skills/stacks-payments/SKILL.md +373 -0
- package/ai/skills/stacks-plan-review/SKILL.md +152 -0
- package/ai/skills/stacks-plugins/SKILL.md +149 -0
- package/ai/skills/stacks-push/SKILL.md +376 -0
- package/ai/skills/stacks-query-builder/SKILL.md +128 -0
- package/ai/skills/stacks-queue/SKILL.md +559 -0
- package/ai/skills/stacks-realtime/SKILL.md +442 -0
- package/ai/skills/stacks-redesign/SKILL.md +182 -0
- package/ai/skills/stacks-registry/SKILL.md +58 -0
- package/ai/skills/stacks-repl/SKILL.md +80 -0
- package/ai/skills/stacks-retro/SKILL.md +122 -0
- package/ai/skills/stacks-review/SKILL.md +135 -0
- package/ai/skills/stacks-router/SKILL.md +231 -0
- package/ai/skills/stacks-routes/SKILL.md +105 -0
- package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
- package/ai/skills/stacks-scheduler/SKILL.md +363 -0
- package/ai/skills/stacks-search-engine/SKILL.md +112 -0
- package/ai/skills/stacks-security/SKILL.md +129 -0
- package/ai/skills/stacks-security-audit/SKILL.md +128 -0
- package/ai/skills/stacks-server/SKILL.md +458 -0
- package/ai/skills/stacks-shell/SKILL.md +62 -0
- package/ai/skills/stacks-slug/SKILL.md +87 -0
- package/ai/skills/stacks-sms/SKILL.md +385 -0
- package/ai/skills/stacks-socials/SKILL.md +303 -0
- package/ai/skills/stacks-storage/SKILL.md +576 -0
- package/ai/skills/stacks-strings/SKILL.md +416 -0
- package/ai/skills/stacks-stx/SKILL.md +176 -0
- package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
- package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
- package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
- package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
- package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
- package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
- package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
- package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
- package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
- package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
- package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
- package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
- package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
- package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
- package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
- package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
- package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
- package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
- package/ai/skills/stacks-testing/SKILL.md +129 -0
- package/ai/skills/stacks-tunnel/SKILL.md +125 -0
- package/ai/skills/stacks-types/SKILL.md +178 -0
- package/ai/skills/stacks-ui/SKILL.md +246 -0
- package/ai/skills/stacks-utils/SKILL.md +444 -0
- package/ai/skills/stacks-validation/SKILL.md +371 -0
- package/ai/skills/stacks-whois/SKILL.md +315 -0
- package/assets/world-map-data.ts +121 -0
- package/bootstrap.ts +114 -0
- package/docs/api-examples.md +46 -0
- package/docs/index.md +24 -0
- package/docs/markdown-examples.md +87 -0
- package/functions/analytics.ts +97 -0
- package/functions/api.ts +143 -0
- package/functions/auth.ts +208 -0
- package/functions/billing/payments.ts +149 -0
- package/functions/blog.ts +120 -0
- package/functions/cms/categorizables.ts +107 -0
- package/functions/cms/pages.ts +108 -0
- package/functions/cms/posts.ts +100 -0
- package/functions/cms/taggables.ts +102 -0
- package/functions/commerce/coupons.ts +128 -0
- package/functions/commerce/customers.ts +128 -0
- package/functions/commerce/gift-cards.ts +119 -0
- package/functions/commerce/orders.ts +112 -0
- package/functions/commerce/payments.ts +119 -0
- package/functions/commerce/products/categories.ts +123 -0
- package/functions/commerce/products/manufacturers.ts +123 -0
- package/functions/commerce/products/products.ts +123 -0
- package/functions/commerce/products/reviews.ts +123 -0
- package/functions/commerce/products/units.ts +123 -0
- package/functions/commerce/products/variants.ts +123 -0
- package/functions/commerce/shippings/delivery-routes.ts +123 -0
- package/functions/commerce/shippings/digital-deliveries.ts +119 -0
- package/functions/commerce/shippings/drivers.ts +123 -0
- package/functions/commerce/shippings/license-keys.ts +123 -0
- package/functions/commerce/shippings/shipping-methods.ts +123 -0
- package/functions/commerce/shippings/shipping-rates.ts +119 -0
- package/functions/commerce/shippings/shipping-zones.ts +119 -0
- package/functions/commerce/taxes.ts +119 -0
- package/functions/commerce/waitlist/products.ts +123 -0
- package/functions/commerce/waitlist/restaurants.ts +123 -0
- package/functions/craft.ts +107 -0
- package/functions/dashboard.ts +153 -0
- package/functions/inbox.ts +95 -0
- package/functions/jobs.ts +86 -0
- package/functions/monitoring/errors.ts +295 -0
- package/functions/queue.ts +111 -0
- package/functions/realtime/websockets.ts +96 -0
- package/functions/realtime.ts +52 -0
- package/functions/releases.ts +49 -0
- package/functions/search.ts +126 -0
- package/functions/toasts.ts +95 -0
- package/functions/uploads.ts +216 -0
- package/ide/.gitignore +4 -0
- package/ide/cursor/rules/auto-imports.mdc +574 -0
- package/ide/cursor/rules/code-style.mdc +12 -0
- package/ide/cursor/rules/documentation.mdc +9 -0
- package/ide/cursor/rules/error-handling-validation.mdc +11 -0
- package/ide/cursor/rules/key-conventions.mdc +11 -0
- package/ide/cursor/rules/project-structure.mdc +169 -0
- package/ide/cursor/rules/readme.mdc +318 -0
- package/ide/cursor/rules/syntax-formatting.mdc +9 -0
- package/ide/cursor/rules/testing.mdc +10 -0
- package/ide/cursor/rules/typescript.mdc +9 -0
- package/ide/cursor/rules/ui-styling.mdc +0 -0
- package/ide/dictionary.txt +397 -0
- package/ide/jetbrains/.fleet/settings.json +4 -0
- package/ide/jetbrains/.idea/.gitignore +5 -0
- package/ide/jetbrains/.idea/.name +1 -0
- package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
- package/ide/jetbrains/.idea/modules.xml +8 -0
- package/ide/jetbrains/.idea/stacks.iml +22 -0
- package/ide/jetbrains/.idea/vcs.xml +6 -0
- package/ide/jetbrains/.idea/webResources.xml +14 -0
- package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
- package/ide/jetbrains/installed.txt +4 -0
- package/ide/jetbrains/options/github-copilot.xml +5 -0
- package/ide/jetbrains/options/nodejs.xml +7 -0
- package/ide/jetbrains/options/templates.xml +124 -0
- package/ide/jetbrains/templates/JavaScript.xml +6 -0
- package/ide/jetbrains/templates/Stacks.xml +8 -0
- package/ide/vscode/.vscode/README.md +23 -0
- package/ide/vscode/.vscode/extensions.json +5 -0
- package/ide/vscode/.vscode/settings.json +176 -0
- package/ide/vscode/.vscode/stacks.code-snippets +181 -0
- package/ide/vscode/LICENSE.md +21 -0
- package/ide/vscode/README.md +36 -0
- package/ide/vscode/logo.png +0 -0
- package/ide/vscode/package.json +79 -0
- package/ide/zed/.zed/settings.json +139 -0
- package/lang/README.md +28 -0
- package/lang/de.yml +14 -0
- package/lang/en.yml +15 -0
- package/package.json +19 -3
- package/project/bootstrap +161 -0
- package/project/buddy +175 -0
- package/project/storage/framework/tsconfig.app.json +50 -0
- package/project/storage/framework/tsconfig.base.json +58 -0
- package/routes/core.ts +78 -0
- package/routes/dashboard-api.ts +233 -0
- package/routes/dashboard.ts +739 -0
- package/routes/email.ts +129 -0
- package/stores/[name].stx +16 -0
- package/stores/git.ts +71 -0
- package/stores/payment.ts +474 -0
- package/stores/queue.ts +43 -0
- package/stores/user.ts +29 -0
- package/stx-components-plugin.ts +25 -0
- package/styles/design-tokens.css +597 -0
- package/types/dashboard.ts +70 -0
- package/types/defaults.ts +438 -0
- package/vcs/github/CODE_OF_CONDUCT.md +83 -0
- package/vcs/github/CONTRIBUTING.md +187 -0
- package/vcs/github/EXPLANATIONS.md +41 -0
- package/vcs/github/FUNDING.yml +1 -0
- package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
- package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
- package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
- package/vcs/github/SECURITY.md +70 -0
- package/vcs/github/renovate.json +5 -0
- package/vcs/github/stale.yml +14 -0
- package/vcs/github/workflows/README.md +16 -0
- package/vcs/github/workflows/ci.yml +114 -0
- package/vcs/github/workflows/export-size.yml +25 -0
- package/vcs/github/workflows/labeler.yml +13 -0
- package/vcs/github/workflows/release.yml +219 -0
- package/views/[...all].stx +8 -0
- package/views/about.md +3 -0
- package/views/auth/login.stx +1 -0
- package/views/auth/signup.stx +85 -0
- package/views/dashboard/.discovered-models.json +542 -0
- package/views/dashboard/AUDIT.md +904 -0
- package/views/dashboard/[...all].stx +89 -0
- package/views/dashboard/access-tokens/index.stx +177 -0
- package/views/dashboard/actions/index.stx +129 -0
- package/views/dashboard/analytics/blog/index.stx +356 -0
- package/views/dashboard/analytics/browsers/index.stx +263 -0
- package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
- package/views/dashboard/analytics/commerce/web/index.stx +379 -0
- package/views/dashboard/analytics/countries/index.stx +289 -0
- package/views/dashboard/analytics/devices/index.stx +305 -0
- package/views/dashboard/analytics/events/index.stx +536 -0
- package/views/dashboard/analytics/index.stx +88 -0
- package/views/dashboard/analytics/marketing/index.stx +315 -0
- package/views/dashboard/analytics/pages/index.stx +271 -0
- package/views/dashboard/analytics/referrers/index.stx +319 -0
- package/views/dashboard/analytics/web/index.stx +356 -0
- package/views/dashboard/buddy/index.stx +198 -0
- package/views/dashboard/ci/index.stx +462 -0
- package/views/dashboard/cloud/index.stx +452 -0
- package/views/dashboard/commands/index.stx +295 -0
- package/views/dashboard/commerce/categories/index.stx +406 -0
- package/views/dashboard/commerce/coupons/index.stx +485 -0
- package/views/dashboard/commerce/customers/index.stx +410 -0
- package/views/dashboard/commerce/dashboard/index.stx +287 -0
- package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
- package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
- package/views/dashboard/commerce/delivery/drivers.stx +397 -0
- package/views/dashboard/commerce/delivery/index.stx +18 -0
- package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
- package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
- package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
- package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
- package/views/dashboard/commerce/delivery/tabs.ts +25 -0
- package/views/dashboard/commerce/gift-cards/index.stx +561 -0
- package/views/dashboard/commerce/manufacturers/index.stx +308 -0
- package/views/dashboard/commerce/orders/index.stx +247 -0
- package/views/dashboard/commerce/payments/index.stx +535 -0
- package/views/dashboard/commerce/pos/index.stx +653 -0
- package/views/dashboard/commerce/printers/devices/index.stx +273 -0
- package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
- package/views/dashboard/commerce/products/detail.stx +563 -0
- package/views/dashboard/commerce/products/index.stx +370 -0
- package/views/dashboard/commerce/reviews/index.stx +656 -0
- package/views/dashboard/commerce/reviews/index.ts +0 -0
- package/views/dashboard/commerce/taxes/index.stx +377 -0
- package/views/dashboard/commerce/units/index.stx +317 -0
- package/views/dashboard/commerce/variants/index.stx +319 -0
- package/views/dashboard/commerce/waitlist/products.stx +942 -0
- package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
- package/views/dashboard/components/index.stx +330 -0
- package/views/dashboard/composables/index.ts +19 -0
- package/views/dashboard/composables/useChart.ts +77 -0
- package/views/dashboard/composables/useNavigation.ts +99 -0
- package/views/dashboard/composables/useRole.ts +58 -0
- package/views/dashboard/composables/useTheme.ts +74 -0
- package/views/dashboard/content/authors/index.stx +171 -0
- package/views/dashboard/content/blog/index.stx +493 -0
- package/views/dashboard/content/categories/index.stx +169 -0
- package/views/dashboard/content/comments/index.stx +189 -0
- package/views/dashboard/content/dashboard.stx +383 -0
- package/views/dashboard/content/files/index.stx +706 -0
- package/views/dashboard/content/pages/index.stx +174 -0
- package/views/dashboard/content/posts/index.stx +269 -0
- package/views/dashboard/content/seo/index.stx +240 -0
- package/views/dashboard/content/tags/index.stx +167 -0
- package/views/dashboard/data/activity/index.stx +261 -0
- package/views/dashboard/data/dashboard/index.stx +116 -0
- package/views/dashboard/data/subscribers.stx +270 -0
- package/views/dashboard/data/teams.stx +275 -0
- package/views/dashboard/data/users.stx +279 -0
- package/views/dashboard/dependencies/index.stx +178 -0
- package/views/dashboard/deployments/[id].stx +234 -0
- package/views/dashboard/deployments/index.stx +9 -0
- package/views/dashboard/dns/index.stx +163 -0
- package/views/dashboard/environment/index.stx +283 -0
- package/views/dashboard/errors/index.stx +274 -0
- package/views/dashboard/forgot-password.stx +57 -0
- package/views/dashboard/functions/index.stx +159 -0
- package/views/dashboard/health/index.stx +248 -0
- package/views/dashboard/inbox/activity.stx +350 -0
- package/views/dashboard/inbox/index.stx +452 -0
- package/views/dashboard/inbox/settings.stx +290 -0
- package/views/dashboard/index.stx +252 -0
- package/views/dashboard/insights/index.stx +529 -0
- package/views/dashboard/jobs/[id].stx +430 -0
- package/views/dashboard/jobs/history.stx +222 -0
- package/views/dashboard/jobs/index.stx +214 -0
- package/views/dashboard/kanban/[id].stx +899 -0
- package/views/dashboard/kanban/index.stx +260 -0
- package/views/dashboard/layouts/default.stx +565 -0
- package/views/dashboard/layouts/guest.stx +10 -0
- package/views/dashboard/login.stx +48 -0
- package/views/dashboard/logs/index.stx +540 -0
- package/views/dashboard/mailboxes/index.stx +149 -0
- package/views/dashboard/management/permissions/index.stx +490 -0
- package/views/dashboard/marketing/campaigns/index.stx +604 -0
- package/views/dashboard/marketing/lists/index.stx +514 -0
- package/views/dashboard/marketing/reviews/index.stx +475 -0
- package/views/dashboard/marketing/social-posts/index.stx +560 -0
- package/views/dashboard/models/[model].stx +195 -0
- package/views/dashboard/models/index.stx +159 -0
- package/views/dashboard/monitoring/errors/index.stx +415 -0
- package/views/dashboard/notifications/dashboard.stx +383 -0
- package/views/dashboard/notifications/email.stx +336 -0
- package/views/dashboard/notifications/history.stx +373 -0
- package/views/dashboard/notifications/sms.stx +332 -0
- package/views/dashboard/packages/index.stx +195 -0
- package/views/dashboard/queries/[id].stx +381 -0
- package/views/dashboard/queries/history.stx +227 -0
- package/views/dashboard/queries/index.stx +335 -0
- package/views/dashboard/queries/slow.stx +318 -0
- package/views/dashboard/queue/index.stx +427 -0
- package/views/dashboard/realtime/index.stx +429 -0
- package/views/dashboard/register.stx +46 -0
- package/views/dashboard/releases/index.stx +389 -0
- package/views/dashboard/requests/index.stx +462 -0
- package/views/dashboard/serverless/index.stx +475 -0
- package/views/dashboard/servers/[id].stx +299 -0
- package/views/dashboard/servers/index.stx +483 -0
- package/views/dashboard/settings/billing.stx +50 -0
- package/views/dashboard/settings/index.stx +256 -0
- package/views/dashboard/settings/mail.stx +252 -0
- package/views/dashboard/stores/auth.ts +123 -0
- package/views/dashboard/stores/ci.ts +342 -0
- package/views/dashboard/stores/index.ts +129 -0
- package/views/dashboard/stores/kanban.ts +723 -0
- package/views/dashboard/stores/rbac.ts +350 -0
- package/views/dashboard/stx.config.ts +34 -0
- package/views/dashboard/teams/[id].stx +170 -0
- package/views/forum/category.stx +92 -0
- package/views/forum/index.stx +209 -0
- package/views/forum/new-topic.stx +113 -0
- package/views/forum/profile.stx +1 -0
- package/views/forum/topic.stx +143 -0
- package/views/hello-world/[name].stx +47 -0
- package/views/index.stx +21 -0
- package/views/system-tray/index.stx +147 -0
|
@@ -0,0 +1,384 @@
|
|
|
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 {
|
|
6
|
+
asArray,
|
|
7
|
+
isFinitePoint,
|
|
8
|
+
rectsOverlap,
|
|
9
|
+
suggestLabelObstacleFix,
|
|
10
|
+
suggestLabelPairFix,
|
|
11
|
+
anchor,
|
|
12
|
+
defaultFromSide,
|
|
13
|
+
defaultToSide,
|
|
14
|
+
chosenSide,
|
|
15
|
+
roundedPath,
|
|
16
|
+
labelPoint,
|
|
17
|
+
arrowClassMap,
|
|
18
|
+
variantAccent
|
|
19
|
+
} from '../shared/geometry.mjs';
|
|
20
|
+
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const { diagram: lifecycle, template, outPath } = loadDiagram({
|
|
23
|
+
rendererDir: __dirname,
|
|
24
|
+
diagramType: 'lifecycle',
|
|
25
|
+
defaultExample: 'agent-run.lifecycle.json'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const viewBox = lifecycle.meta?.viewBox || [980, 660];
|
|
29
|
+
const layout = {
|
|
30
|
+
phaseY: 126,
|
|
31
|
+
eventY: 278,
|
|
32
|
+
outcomeY: 450,
|
|
33
|
+
phaseW: 118,
|
|
34
|
+
phaseH: 62,
|
|
35
|
+
eventW: 126,
|
|
36
|
+
eventH: 58,
|
|
37
|
+
outcomeW: 118,
|
|
38
|
+
outcomeH: 58,
|
|
39
|
+
phaseXs: [94, 248, 402, 556, 710],
|
|
40
|
+
eventXs: [402, 556, 710],
|
|
41
|
+
outcomeXs: [402, 556, 710]
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const typeClass = {
|
|
45
|
+
start: 'c-frontend',
|
|
46
|
+
active: 'c-backend',
|
|
47
|
+
waiting: 'c-cloud',
|
|
48
|
+
decision: 'c-security',
|
|
49
|
+
success: 'c-database',
|
|
50
|
+
failure: 'c-security',
|
|
51
|
+
neutral: 'c-external',
|
|
52
|
+
external: 'c-external'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const textClass = {
|
|
56
|
+
start: 't-frontend',
|
|
57
|
+
active: 't-backend',
|
|
58
|
+
waiting: 't-cloud',
|
|
59
|
+
decision: 't-security',
|
|
60
|
+
success: 't-database',
|
|
61
|
+
failure: 't-security',
|
|
62
|
+
neutral: 't-muted',
|
|
63
|
+
external: 't-muted'
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function legendY() {
|
|
67
|
+
return viewBox[1] - 98;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Lane semantics are fixed: lane id "main" maps to the top phase band, lane id
|
|
71
|
+
// "terminal" maps to the bottom outcome band, and every other lane shares the
|
|
72
|
+
// middle event band (separated visually via yOffset).
|
|
73
|
+
function bandFor(lane) {
|
|
74
|
+
if (lane === 'main') return 'phase';
|
|
75
|
+
if (lane === 'terminal') return 'outcome';
|
|
76
|
+
return 'event';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function measureState(state) {
|
|
80
|
+
const isPhase = bandFor(state.lane) === 'phase';
|
|
81
|
+
const isOutcome = bandFor(state.lane) === 'outcome';
|
|
82
|
+
const width = state.width || (isPhase ? layout.phaseW : isOutcome ? layout.outcomeW : layout.eventW);
|
|
83
|
+
const height = state.height || (isPhase ? layout.phaseH : isOutcome ? layout.outcomeH : layout.eventH);
|
|
84
|
+
const xs = isPhase ? layout.phaseXs : isOutcome ? layout.outcomeXs : layout.eventXs;
|
|
85
|
+
const cx = xs[state.col] ?? xs[xs.length - 1];
|
|
86
|
+
const y = (
|
|
87
|
+
isPhase ? layout.phaseY :
|
|
88
|
+
isOutcome ? layout.outcomeY :
|
|
89
|
+
layout.eventY
|
|
90
|
+
) + (state.yOffset || 0);
|
|
91
|
+
return {
|
|
92
|
+
...state,
|
|
93
|
+
width,
|
|
94
|
+
height,
|
|
95
|
+
x: cx - width / 2,
|
|
96
|
+
y,
|
|
97
|
+
cx,
|
|
98
|
+
cy: y + height / 2
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const states = new Map(asArray(lifecycle.states).map((state) => [state.id, measureState(state)]));
|
|
103
|
+
const stateSteps = new Map();
|
|
104
|
+
for (const [index, transition] of asArray(lifecycle.transitions).entries()) {
|
|
105
|
+
if (!stateSteps.has(transition.from)) stateSteps.set(transition.from, index);
|
|
106
|
+
if (!stateSteps.has(transition.to)) stateSteps.set(transition.to, index + 1);
|
|
107
|
+
}
|
|
108
|
+
for (const [index, state] of asArray(lifecycle.states).entries()) {
|
|
109
|
+
if (!stateSteps.has(state.id)) stateSteps.set(state.id, index);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function validateLifecycle() {
|
|
113
|
+
const problems = [];
|
|
114
|
+
if (lifecycle.schema_version !== 1) problems.push('Lifecycle files must set "schema_version": 1.');
|
|
115
|
+
if (lifecycle.diagram_type !== 'lifecycle') problems.push('Lifecycle files must set "diagram_type": "lifecycle".');
|
|
116
|
+
if (!lifecycle.meta?.title) problems.push('Lifecycle files must include meta.title.');
|
|
117
|
+
if (!Array.isArray(lifecycle.lanes) || lifecycle.lanes.length < 1) problems.push('Lifecycle diagrams need at least one lane.');
|
|
118
|
+
if (!Array.isArray(lifecycle.states) || lifecycle.states.length < 2) problems.push('Lifecycle diagrams need at least two states.');
|
|
119
|
+
if (!Array.isArray(lifecycle.transitions)) problems.push('Lifecycle diagrams must include a transitions array.');
|
|
120
|
+
if (lifecycle.cards !== undefined && !Array.isArray(lifecycle.cards)) problems.push('Lifecycle "cards" must be an array.');
|
|
121
|
+
if (states.size !== asArray(lifecycle.states).length) problems.push('State ids must be unique.');
|
|
122
|
+
|
|
123
|
+
// The three bands are fixed at y=112/264/436; the legend (viewBox[1] - 98)
|
|
124
|
+
// must clear the outcome band's header zone.
|
|
125
|
+
if (legendY() - 20 < 448) {
|
|
126
|
+
problems.push(`viewBox height ${viewBox[1]} is too short for the fixed band layout - set meta.viewBox[1] to at least 566.`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const laneIds = new Set(asArray(lifecycle.lanes).map((lane) => lane.id));
|
|
130
|
+
if (laneIds.size !== asArray(lifecycle.lanes).length) problems.push('Lane ids must be unique.');
|
|
131
|
+
if (!laneIds.has('main')) {
|
|
132
|
+
problems.push('Lifecycle diagrams need a lane with id "main" (the phase rail). Lane ids "main" and "terminal" are reserved: "main" maps to the top phase band, "terminal" to the bottom outcome band, and all other lanes share the middle event band.');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const state of states.values()) {
|
|
136
|
+
if (!laneIds.has(state.lane)) {
|
|
137
|
+
problems.push(`State "${state.id}" uses unknown lane "${state.lane}".`);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const band = bandFor(state.lane);
|
|
141
|
+
const maxCol = band === 'phase'
|
|
142
|
+
? layout.phaseXs.length
|
|
143
|
+
: band === 'outcome'
|
|
144
|
+
? layout.outcomeXs.length
|
|
145
|
+
: layout.eventXs.length;
|
|
146
|
+
if (!Number.isInteger(state.col) || state.col < 0 || state.col >= maxCol) {
|
|
147
|
+
problems.push(`State "${state.id}" uses invalid column ${state.col} - the ${band} band has integer columns 0..${maxCol - 1}.`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (!isFinitePoint(state.x, state.y, state.cx, state.cy)) {
|
|
151
|
+
problems.push(`State "${state.id}" produced non-finite coordinates - check col, width, height, and yOffset are numbers.`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (state.x < 32 || state.x + state.width > viewBox[0] - 32) {
|
|
155
|
+
problems.push(`State "${state.id}" exceeds the horizontal bounds of the diagram - reduce state.width or increase meta.viewBox[0].`);
|
|
156
|
+
}
|
|
157
|
+
if (state.y < 64 || state.y + state.height > legendY() - 24) {
|
|
158
|
+
problems.push(`State "${state.id}" exceeds the vertical lifecycle area - keep y between 64 and ${legendY() - 24} (adjust yOffset or increase meta.viewBox[1]).`);
|
|
159
|
+
}
|
|
160
|
+
const estLabelW = textUnits(state.label) * 6.2;
|
|
161
|
+
if (estLabelW > state.width + 6) {
|
|
162
|
+
problems.push(`Label "${state.label}" (~${Math.round(estLabelW)}px) is wider than state "${state.id}" (${state.width}px) - shorten the label, move detail to sublabel, or increase state.width.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// All non-main/non-terminal lanes share the same y band, so the overlap
|
|
167
|
+
// check must run across lanes - not per-lane.
|
|
168
|
+
const allStates = [...states.values()];
|
|
169
|
+
for (let i = 0; i < allStates.length; i += 1) {
|
|
170
|
+
for (let j = i + 1; j < allStates.length; j += 1) {
|
|
171
|
+
if (rectsOverlap(allStates[i], allStates[j], 10)) {
|
|
172
|
+
problems.push(`States "${allStates[i].id}" and "${allStates[j].id}" are less than 10px apart - move one to another col or separate them with yOffset (lanes other than "main"/"terminal" share one band).`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (const transition of asArray(lifecycle.transitions)) {
|
|
178
|
+
if (!states.has(transition.from)) problems.push(`Transition "${transition.label || transition.from}" references unknown source "${transition.from}".`);
|
|
179
|
+
if (!states.has(transition.to)) problems.push(`Transition "${transition.label || transition.to}" references unknown target "${transition.to}".`);
|
|
180
|
+
if (states.has(transition.from) && states.has(transition.to)) {
|
|
181
|
+
const routed = pathFor(transition);
|
|
182
|
+
const [start, end] = [routed.points[0], routed.points[routed.points.length - 1]];
|
|
183
|
+
const distance = Math.hypot(end[0] - start[0], end[1] - start[1]);
|
|
184
|
+
if (distance < 32) problems.push(`Transition "${transition.label || `${transition.from}->${transition.to}`}" is too short (${Math.round(distance)}px; minimum 32px) - route it through a channel or drop its label.`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const labelRects = [];
|
|
189
|
+
for (const transition of asArray(lifecycle.transitions)) {
|
|
190
|
+
if (!transition.label || !states.has(transition.from) || !states.has(transition.to)) continue;
|
|
191
|
+
const [lx, ly] = labelPoint(transition, pathFor(transition).points);
|
|
192
|
+
const longestLine = Math.max(textUnits(transition.label), textUnits(transition.note || ''));
|
|
193
|
+
const width = Math.max(32, longestLine * 4.9 + 12);
|
|
194
|
+
const height = transition.note ? 27 : 16;
|
|
195
|
+
labelRects.push({ label: transition.label, x: lx - width / 2, y: ly - 11, width, height, lx, ly });
|
|
196
|
+
}
|
|
197
|
+
for (const rect of labelRects) {
|
|
198
|
+
for (const state of states.values()) {
|
|
199
|
+
if (rectsOverlap(rect, state, -2)) {
|
|
200
|
+
problems.push(`Label "${rect.label}" overlaps state "${state.id}" - adjust labelDx/labelDy/labelSegment or set labelAt.\n${suggestLabelObstacleFix(rect, rect.lx, rect.ly, state, 'state')}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (let i = 0; i < labelRects.length; i += 1) {
|
|
205
|
+
for (let j = i + 1; j < labelRects.length; j += 1) {
|
|
206
|
+
if (rectsOverlap(labelRects[i], labelRects[j], -2)) {
|
|
207
|
+
problems.push(`Labels "${labelRects[i].label}" and "${labelRects[j].label}" overlap - adjust labelDx/labelDy.\n${suggestLabelPairFix(labelRects[i], labelRects[j])}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (problems.length) {
|
|
213
|
+
throw new Error(`Lifecycle layout validation failed:\n- ${problems.join('\n- ')}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function routeVia(transition, from, to, start, end) {
|
|
218
|
+
if (transition.via) return transition.via;
|
|
219
|
+
switch (transition.route || 'auto') {
|
|
220
|
+
case 'straight':
|
|
221
|
+
return [];
|
|
222
|
+
case 'drop': {
|
|
223
|
+
const y = transition.channelY ?? (start[1] + end[1]) / 2;
|
|
224
|
+
return [[start[0], y], [end[0], y]];
|
|
225
|
+
}
|
|
226
|
+
case 'bottom-channel': {
|
|
227
|
+
const y = transition.channelY ?? Math.max(from.y + from.height, to.y + to.height) + 34;
|
|
228
|
+
return [[start[0], y], [end[0], y]];
|
|
229
|
+
}
|
|
230
|
+
case 'top-channel': {
|
|
231
|
+
const y = transition.channelY ?? Math.min(from.y, to.y) - 28;
|
|
232
|
+
return [[start[0], y], [end[0], y]];
|
|
233
|
+
}
|
|
234
|
+
case 'right-channel': {
|
|
235
|
+
const x = transition.channelX ?? Math.max(from.x + from.width, to.x + to.width) + 36;
|
|
236
|
+
return [[x, start[1]], [x, end[1]]];
|
|
237
|
+
}
|
|
238
|
+
case 'left-channel': {
|
|
239
|
+
const x = transition.channelX ?? Math.min(from.x, to.x) - 36;
|
|
240
|
+
return [[x, start[1]], [x, end[1]]];
|
|
241
|
+
}
|
|
242
|
+
case 'auto':
|
|
243
|
+
default: {
|
|
244
|
+
if (from.lane === to.lane) return [];
|
|
245
|
+
const y = transition.channelY ?? (start[1] + end[1]) / 2;
|
|
246
|
+
return [[start[0], y], [end[0], y]];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const pathCache = new Map();
|
|
252
|
+
|
|
253
|
+
function pathFor(transition) {
|
|
254
|
+
if (pathCache.has(transition)) return pathCache.get(transition);
|
|
255
|
+
const from = states.get(transition.from);
|
|
256
|
+
const to = states.get(transition.to);
|
|
257
|
+
const start = anchor(from, chosenSide(transition.fromSide, defaultFromSide(from, to)));
|
|
258
|
+
const end = anchor(to, chosenSide(transition.toSide, defaultToSide(from, to)));
|
|
259
|
+
const points = [start, ...routeVia(transition, from, to, start, end), end];
|
|
260
|
+
const routed = {
|
|
261
|
+
d: roundedPath(points, transition.cornerRadius ?? 10),
|
|
262
|
+
points
|
|
263
|
+
};
|
|
264
|
+
pathCache.set(transition, routed);
|
|
265
|
+
return routed;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function bandTitles() {
|
|
269
|
+
const lanes = asArray(lifecycle.lanes);
|
|
270
|
+
const mainLane = lanes.find((lane) => lane.id === 'main');
|
|
271
|
+
const terminalLane = lanes.find((lane) => lane.id === 'terminal');
|
|
272
|
+
const eventLanes = lanes.filter((lane) => lane.id !== 'main' && lane.id !== 'terminal');
|
|
273
|
+
return [
|
|
274
|
+
mainLane?.label || 'Lifecycle phases',
|
|
275
|
+
eventLanes.length ? eventLanes.map((lane) => lane.label).join(' + ') : 'Interruptions + recovery',
|
|
276
|
+
terminalLane?.label || 'Outcomes'
|
|
277
|
+
];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function renderBands() {
|
|
281
|
+
const right = viewBox[0] - 72;
|
|
282
|
+
const titles = bandTitles();
|
|
283
|
+
return ` <path d="M 72 112 L ${right} 112" class="a-default" stroke-width="0.8" stroke-dasharray="3,8"/>
|
|
284
|
+
<text x="72" y="100" class="t-dim" font-size="10" font-weight="600">01 / ${esc(titles[0])}</text>
|
|
285
|
+
<path d="M 72 264 L ${right} 264" class="a-default" stroke-width="0.8" stroke-dasharray="3,8"/>
|
|
286
|
+
<text x="72" y="252" class="t-dim" font-size="10" font-weight="600">02 / ${esc(titles[1])}</text>
|
|
287
|
+
<path d="M 72 436 L ${right} 436" class="a-default" stroke-width="0.8" stroke-dasharray="3,8"/>
|
|
288
|
+
<text x="72" y="424" class="t-dim" font-size="10" font-weight="600">03 / ${esc(titles[2])}</text>`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function renderState(state) {
|
|
292
|
+
const fill = typeClass[state.type] || typeClass.neutral;
|
|
293
|
+
const accent = textClass[state.type] || 't-muted';
|
|
294
|
+
const tag = state.tag
|
|
295
|
+
? `\n <text x="${state.cx}" y="${state.y + state.height - 11}" class="${accent}" font-size="7" text-anchor="middle">${esc(state.tag)}</text>`
|
|
296
|
+
: '';
|
|
297
|
+
const step = state.step
|
|
298
|
+
? `\n <text x="${state.x + 10}" y="${state.y + 14}" class="${accent}" font-size="7" font-weight="700">${esc(state.step)}</text>`
|
|
299
|
+
: '';
|
|
300
|
+
return ` <rect x="${state.x}" y="${state.y}" width="${state.width}" height="${state.height}" rx="7" class="c-mask"/>
|
|
301
|
+
<rect x="${state.x}" y="${state.y}" width="${state.width}" height="${state.height}" rx="7" class="${fill}"${animateAttr(lifecycle.meta, 'node', stateSteps.get(state.id))} stroke-width="1.5"/>${step}
|
|
302
|
+
<text x="${state.cx}" y="${state.y + 21}" class="t-primary" font-size="10" font-weight="600" text-anchor="middle">${esc(state.label)}</text>
|
|
303
|
+
<text x="${state.cx}" y="${state.y + 37}" class="t-muted" font-size="7" text-anchor="middle">${esc(state.sublabel || '')}</text>${tag}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function renderTransitionPath(transition, index) {
|
|
307
|
+
const [cls, marker] = arrowClassMap[transition.variant || 'default'] || arrowClassMap.default;
|
|
308
|
+
const routed = pathFor(transition);
|
|
309
|
+
const strokeWidth = transition.width || (transition.variant === 'emphasis' ? 2 : 1.1);
|
|
310
|
+
return ` <path d="${routed.d}" class="${cls}"${animateAttr(lifecycle.meta, 'edge', index)} stroke-width="${strokeWidth}" marker-end="url(#${marker})"/>`;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function renderTransitionLabel(transition) {
|
|
314
|
+
if (!transition.label) return '';
|
|
315
|
+
const routed = pathFor(transition);
|
|
316
|
+
const [lx, ly] = labelPoint(transition, routed.points);
|
|
317
|
+
const longestLine = Math.max(textUnits(transition.label), textUnits(transition.note || ''));
|
|
318
|
+
const labelW = Math.max(32, longestLine * 4.9 + 12);
|
|
319
|
+
const labelH = transition.note ? 27 : 16;
|
|
320
|
+
const note = transition.note
|
|
321
|
+
? `\n <text x="${lx}" y="${ly + 11}" class="t-dim" font-size="7" text-anchor="middle">${esc(transition.note)}</text>`
|
|
322
|
+
: '';
|
|
323
|
+
return ` <rect x="${lx - labelW / 2}" y="${ly - 11}" width="${labelW}" height="${labelH}" rx="4" class="c-mask"/>
|
|
324
|
+
<text x="${lx}" y="${ly}" class="${variantAccent(transition.variant)}" font-size="8" text-anchor="middle">${esc(transition.label)}</text>${note}`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function renderLegend() {
|
|
328
|
+
const y = legendY();
|
|
329
|
+
return ` <text x="220" y="${y - 20}" class="t-primary" font-size="10" font-weight="600">Legend</text>
|
|
330
|
+
<rect x="220" y="${y - 8}" width="14" height="9" rx="2" class="c-backend" stroke-width="1"/>
|
|
331
|
+
<text x="240" y="${y}" class="t-muted" font-size="7">active state</text>
|
|
332
|
+
<rect x="325" y="${y - 8}" width="14" height="9" rx="2" class="c-cloud" stroke-width="1"/>
|
|
333
|
+
<text x="345" y="${y}" class="t-muted" font-size="7">waiting</text>
|
|
334
|
+
<rect x="415" y="${y - 8}" width="14" height="9" rx="2" class="c-database" stroke-width="1"/>
|
|
335
|
+
<text x="435" y="${y}" class="t-muted" font-size="7">terminal success</text>
|
|
336
|
+
<rect x="560" y="${y - 8}" width="14" height="9" rx="2" class="c-security" stroke-width="1"/>
|
|
337
|
+
<text x="580" y="${y}" class="t-muted" font-size="7">failure / exit</text>`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function renderLifecycleRail() {
|
|
341
|
+
const mainCols = [...states.values()]
|
|
342
|
+
.filter((state) => bandFor(state.lane) === 'phase')
|
|
343
|
+
.map((state) => state.col);
|
|
344
|
+
if (!mainCols.length) return '';
|
|
345
|
+
const railEnd = layout.phaseXs[Math.max(...mainCols)] + 38;
|
|
346
|
+
return ` <path d="M 154 ${layout.phaseY + 31} L ${railEnd} ${layout.phaseY + 31}" class="a-emphasis" stroke-width="2.2" marker-end="url(#arrowhead-emphasis)"/>`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function renderSvg() {
|
|
350
|
+
return ` <svg viewBox="0 0 ${viewBox[0]} ${viewBox[1]}" ${svgRootAttrs(lifecycle.meta, 'lifecycle diagram')}>
|
|
351
|
+
${renderDefinitions()}
|
|
352
|
+
|
|
353
|
+
<!-- Background Grid -->
|
|
354
|
+
<rect width="100%" height="100%" fill="url(#grid)" />
|
|
355
|
+
|
|
356
|
+
<!-- Lifecycle bands -->
|
|
357
|
+
${renderBands()}
|
|
358
|
+
|
|
359
|
+
<!-- Primary lifecycle rail -->
|
|
360
|
+
${renderLifecycleRail()}
|
|
361
|
+
|
|
362
|
+
<!-- Transition paths -->
|
|
363
|
+
${asArray(lifecycle.transitions).map(renderTransitionPath).join('\n')}
|
|
364
|
+
|
|
365
|
+
<!-- States -->
|
|
366
|
+
${[...states.values()].map(renderState).join('\n\n')}
|
|
367
|
+
|
|
368
|
+
<!-- Transition labels -->
|
|
369
|
+
${asArray(lifecycle.transitions).map(renderTransitionLabel).join('\n')}
|
|
370
|
+
|
|
371
|
+
<!-- Legend -->
|
|
372
|
+
${renderLegend()}
|
|
373
|
+
</svg>`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
validateLifecycle();
|
|
377
|
+
writeDiagram({
|
|
378
|
+
outPath,
|
|
379
|
+
template,
|
|
380
|
+
meta: lifecycle.meta,
|
|
381
|
+
footerLabel: 'Lifecycle diagram',
|
|
382
|
+
svg: renderSvg(),
|
|
383
|
+
cards: lifecycle.cards,
|
|
384
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Sequence Renderer
|
|
2
|
+
|
|
3
|
+
Render `diagram_type: "sequence"` 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/sequence/render-sequence.mjs input.sequence.json output.html
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The renderer validates input against `.claude/skills/stacks-technical-diagrams/schemas/sequence.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 `sequence.html` in the current working directory.
|
|
15
|
+
|
|
16
|
+
## Input
|
|
17
|
+
|
|
18
|
+
Sequence JSON files must set:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"schema_version": 1,
|
|
23
|
+
"diagram_type": "sequence",
|
|
24
|
+
"meta": {
|
|
25
|
+
"title": "Cache Miss Request Sequence",
|
|
26
|
+
"subtitle": "Frontend request path with auth and cache fallback",
|
|
27
|
+
"viewBox": [920, 760]
|
|
28
|
+
},
|
|
29
|
+
"participants": [],
|
|
30
|
+
"segments": [],
|
|
31
|
+
"messages": [],
|
|
32
|
+
"activations": [],
|
|
33
|
+
"cards": []
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The timeline scales with the viewBox height: a taller `meta.viewBox` buys more
|
|
38
|
+
message room, a shorter one shrinks the readable band instead of clipping. A
|
|
39
|
+
complete worked example lives at
|
|
40
|
+
`.claude/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json`.
|
|
41
|
+
|
|
42
|
+
The schema lives at:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
.claude/skills/stacks-technical-diagrams/schemas/sequence.schema.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Layout budget
|
|
49
|
+
|
|
50
|
+
| Constant | Value |
|
|
51
|
+
|----------|-------|
|
|
52
|
+
| viewBox | default `[920, 760]`; schema minimum `[480, 480]` |
|
|
53
|
+
| Participant boxes | 86×54 at y 72; centers at x = 62 + index×108 |
|
|
54
|
+
| Participant count | last center + 43 must be ≤ width − 40 (8 fit at width 920) |
|
|
55
|
+
| Lifelines | from y 142 down to height − 65; band must be ≥120px tall |
|
|
56
|
+
| Message `y` range | `[160, height − 83]` |
|
|
57
|
+
| Message spacing | ≥28px vertical between messages that share horizontal space |
|
|
58
|
+
| Arrow span | ≥60px horizontal between the two participants |
|
|
59
|
+
| Segments | y pixel ranges with `to > from`, inside `[72, lifeline bottom + 20]` |
|
|
60
|
+
| Legend row | y = height − 54 |
|
|
61
|
+
|
|
62
|
+
`segments[].from/to` and `activations[].from/to` are y pixel coordinates, not
|
|
63
|
+
participant ids; activations also require `to > from`.
|
|
64
|
+
|
|
65
|
+
## Design Rules
|
|
66
|
+
|
|
67
|
+
- Put participants across the top, ordered by the story the reader should
|
|
68
|
+
follow.
|
|
69
|
+
- Time moves downward.
|
|
70
|
+
- Use `emphasis` for the main request path.
|
|
71
|
+
- Use `security` for auth, consent, permission, and policy calls.
|
|
72
|
+
- Use `return` for quiet response messages.
|
|
73
|
+
- Use `dashed` for async trace, event, logging, and non-blocking work.
|
|
74
|
+
- Use segments as light background guides; keep segment labels short.
|
|
75
|
+
- Keep labels short enough to fit in narrow previews.
|
|
76
|
+
|
|
77
|
+
Schema violations exit non-zero with path-prefixed messages annotated with the
|
|
78
|
+
element's id or label. The renderer additionally fails when it can detect
|
|
79
|
+
layout problems, including missing participants, duplicate participant IDs,
|
|
80
|
+
participant labels wider than their box, unknown message endpoints, messages
|
|
81
|
+
outside the readable timeline, overly tight vertical spacing between messages
|
|
82
|
+
that overlap horizontally, invalid segment or activation ranges, or
|
|
83
|
+
participants that exceed the viewBox. Text width is estimated CJK-aware:
|
|
84
|
+
fullwidth glyphs count as two units.
|