@stacksjs/defaults 0.70.207 → 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,481 @@
|
|
|
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
|
+
segmentIntersectsRect,
|
|
10
|
+
suggestLabelObstacleFix,
|
|
11
|
+
suggestLabelPairFix,
|
|
12
|
+
anchor,
|
|
13
|
+
defaultFromSide,
|
|
14
|
+
defaultToSide,
|
|
15
|
+
chosenSide,
|
|
16
|
+
polylinePath,
|
|
17
|
+
labelPoint,
|
|
18
|
+
componentFill,
|
|
19
|
+
componentText,
|
|
20
|
+
arrowClassMap,
|
|
21
|
+
variantAccent
|
|
22
|
+
} from '../shared/geometry.mjs';
|
|
23
|
+
|
|
24
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const { diagram: workflow, template, outPath } = loadDiagram({
|
|
26
|
+
rendererDir: __dirname,
|
|
27
|
+
diagramType: 'workflow',
|
|
28
|
+
defaultExample: 'agent-tool-call.workflow.json'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const layout = {
|
|
32
|
+
laneX: 40,
|
|
33
|
+
laneY: 52,
|
|
34
|
+
laneW: 640,
|
|
35
|
+
laneH: 104,
|
|
36
|
+
laneGap: 20,
|
|
37
|
+
laneTitleH: 30,
|
|
38
|
+
colXs: [88, 220, 300, 430, 500, 625],
|
|
39
|
+
nodeW: 92,
|
|
40
|
+
nodeH: 52
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Content is 680px wide (laneX + laneW); auto height fits the lanes plus legend.
|
|
44
|
+
const autoHeight = layout.laneY
|
|
45
|
+
+ (workflow.lanes?.length || 1) * layout.laneH
|
|
46
|
+
+ ((workflow.lanes?.length || 1) - 1) * layout.laneGap
|
|
47
|
+
+ 124;
|
|
48
|
+
const viewBox = workflow.meta?.viewBox || [720, autoHeight];
|
|
49
|
+
|
|
50
|
+
const laneIndex = new Map(asArray(workflow.lanes).map((lane, index) => [lane.id, index]));
|
|
51
|
+
|
|
52
|
+
function laneTop(id) {
|
|
53
|
+
return layout.laneY + laneIndex.get(id) * (layout.laneH + layout.laneGap);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function lastLaneBottom() {
|
|
57
|
+
return layout.laneY + workflow.lanes.length * layout.laneH + (workflow.lanes.length - 1) * layout.laneGap;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function legendY() {
|
|
61
|
+
return lastLaneBottom() + 44;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function measureNode(node) {
|
|
65
|
+
const width = node.width || layout.nodeW;
|
|
66
|
+
const height = node.height || (node.tag ? 68 : layout.nodeH);
|
|
67
|
+
const cx = layout.colXs[node.col];
|
|
68
|
+
const contentH = layout.laneH - layout.laneTitleH;
|
|
69
|
+
const y = laneTop(node.lane) + layout.laneTitleH + (contentH - height) / 2 + (node.yOffset || 0);
|
|
70
|
+
return {
|
|
71
|
+
...node,
|
|
72
|
+
width,
|
|
73
|
+
height,
|
|
74
|
+
x: cx - width / 2,
|
|
75
|
+
y,
|
|
76
|
+
cx,
|
|
77
|
+
cy: y + height / 2
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const nodes = new Map(asArray(workflow.nodes).map((node) => [node.id, measureNode(node)]));
|
|
82
|
+
|
|
83
|
+
const mainPathSteps = new Map(asArray(workflow.mainPath).map((id, index) => [id, index]));
|
|
84
|
+
const edgeSteps = new Map(asArray(workflow.edges).map((edge, index) => {
|
|
85
|
+
const fromStep = mainPathSteps.get(edge.from);
|
|
86
|
+
const toStep = mainPathSteps.get(edge.to);
|
|
87
|
+
const mainStep = Number.isInteger(fromStep) && toStep === fromStep + 1 ? fromStep : null;
|
|
88
|
+
return [edge, mainStep ?? asArray(workflow.mainPath).length + index];
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
function nodeStep(node) {
|
|
92
|
+
return mainPathSteps.get(node.id) ?? asArray(workflow.mainPath).length + asArray(workflow.nodes).findIndex((item) => item.id === node.id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function validateWorkflow() {
|
|
96
|
+
const problems = [];
|
|
97
|
+
if (workflow.schema_version !== 1) {
|
|
98
|
+
problems.push('Workflow files must set "schema_version": 1.');
|
|
99
|
+
}
|
|
100
|
+
if (workflow.diagram_type !== 'workflow') {
|
|
101
|
+
problems.push(`Unsupported diagram_type "${workflow.diagram_type}". Expected "workflow".`);
|
|
102
|
+
}
|
|
103
|
+
if (!workflow.meta || !workflow.meta.title) {
|
|
104
|
+
problems.push('Workflow files must include meta.title.');
|
|
105
|
+
}
|
|
106
|
+
if (!Array.isArray(workflow.lanes) || !workflow.lanes.length) {
|
|
107
|
+
problems.push('Workflow files must include at least one lane.');
|
|
108
|
+
}
|
|
109
|
+
if (!Array.isArray(workflow.nodes)) {
|
|
110
|
+
problems.push('Workflow files must include a nodes array.');
|
|
111
|
+
}
|
|
112
|
+
if (!Array.isArray(workflow.edges)) {
|
|
113
|
+
problems.push('Workflow files must include an edges array.');
|
|
114
|
+
}
|
|
115
|
+
if (workflow.phases !== undefined && !Array.isArray(workflow.phases)) {
|
|
116
|
+
problems.push('Workflow "phases" must be an array.');
|
|
117
|
+
}
|
|
118
|
+
if (workflow.groups !== undefined && !Array.isArray(workflow.groups)) {
|
|
119
|
+
problems.push('Workflow "groups" must be an array.');
|
|
120
|
+
}
|
|
121
|
+
if (workflow.mainPath !== undefined && !Array.isArray(workflow.mainPath)) {
|
|
122
|
+
problems.push('Workflow "mainPath" must be an array of node ids.');
|
|
123
|
+
}
|
|
124
|
+
if (workflow.cards !== undefined && !Array.isArray(workflow.cards)) {
|
|
125
|
+
problems.push('Workflow "cards" must be an array.');
|
|
126
|
+
}
|
|
127
|
+
if (problems.length) {
|
|
128
|
+
throw new Error(`Workflow layout validation failed:\n- ${problems.join('\n- ')}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const laneIds = new Set(workflow.lanes.map((lane) => lane.id));
|
|
132
|
+
if (laneIds.size !== workflow.lanes.length) {
|
|
133
|
+
problems.push('Lane ids must be unique.');
|
|
134
|
+
}
|
|
135
|
+
if (nodes.size !== workflow.nodes.length) {
|
|
136
|
+
problems.push('Node ids must be unique.');
|
|
137
|
+
}
|
|
138
|
+
const phaseIds = new Set(asArray(workflow.phases).map((phase) => phase.id));
|
|
139
|
+
if (phaseIds.size !== asArray(workflow.phases).length) {
|
|
140
|
+
problems.push('Phase ids must be unique.');
|
|
141
|
+
}
|
|
142
|
+
const groupIds = new Set(asArray(workflow.groups).map((group) => group.id));
|
|
143
|
+
if (groupIds.size !== asArray(workflow.groups).length) {
|
|
144
|
+
problems.push('Group ids must be unique.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const node of nodes.values()) {
|
|
148
|
+
if (!laneIds.has(node.lane)) {
|
|
149
|
+
problems.push(`Node "${node.id}" uses unknown lane "${node.lane}".`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (!Number.isInteger(node.col) || node.col < 0 || node.col >= layout.colXs.length) {
|
|
153
|
+
problems.push(`Node "${node.id}" uses column ${node.col}, but valid columns are integers 0..${layout.colXs.length - 1}.`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (!isFinitePoint(node.x, node.y, node.cx, node.cy)) {
|
|
157
|
+
problems.push(`Node "${node.id}" produced non-finite coordinates - check col, width, height, and yOffset are numbers.`);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const estLabelW = textUnits(node.label) * 6.8;
|
|
161
|
+
if (estLabelW > node.width + 6) {
|
|
162
|
+
problems.push(`Label "${node.label}" (~${Math.round(estLabelW)}px) is wider than node "${node.id}" (${node.width}px) - shorten the label, move detail to sublabel, or increase node.width.`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const top = laneTop(node.lane);
|
|
166
|
+
const contentTop = top + layout.laneTitleH;
|
|
167
|
+
const laneRight = layout.laneX + layout.laneW;
|
|
168
|
+
if (node.x < layout.laneX || node.x + node.width > laneRight) {
|
|
169
|
+
problems.push(`Node "${node.id}" exceeds the horizontal bounds of lane "${node.lane}".`);
|
|
170
|
+
}
|
|
171
|
+
if (node.y < contentTop || node.y + node.height > top + layout.laneH) {
|
|
172
|
+
problems.push(`Node "${node.id}" collides with the title or boundary of lane "${node.lane}".`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const phase of asArray(workflow.phases)) {
|
|
177
|
+
if (!Number.isInteger(phase.fromCol) || !Number.isInteger(phase.toCol)) {
|
|
178
|
+
problems.push(`Phase "${phase.id}" must use integer fromCol/toCol values.`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (phase.fromCol < 0 || phase.toCol >= layout.colXs.length || phase.fromCol > phase.toCol) {
|
|
182
|
+
problems.push(`Phase "${phase.id}" uses invalid columns ${phase.fromCol}..${phase.toCol}; use an ordered range within 0..${layout.colXs.length - 1}.`);
|
|
183
|
+
}
|
|
184
|
+
const estLabelW = textUnits(phase.label) * 5.6;
|
|
185
|
+
const width = spanForCols(phase.fromCol, phase.toCol).width;
|
|
186
|
+
if (estLabelW > width + 8) {
|
|
187
|
+
problems.push(`Phase label "${phase.label}" (~${Math.round(estLabelW)}px) is wider than its ${Math.round(width)}px span - shorten the label or widen the phase range.`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for (const group of asArray(workflow.groups)) {
|
|
192
|
+
if (!laneIds.has(group.lane)) {
|
|
193
|
+
problems.push(`Group "${group.id}" uses unknown lane "${group.lane}".`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (!Number.isInteger(group.fromCol) || !Number.isInteger(group.toCol)) {
|
|
197
|
+
problems.push(`Group "${group.id}" must use integer fromCol/toCol values.`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (group.fromCol < 0 || group.toCol >= layout.colXs.length || group.fromCol > group.toCol) {
|
|
201
|
+
problems.push(`Group "${group.id}" uses invalid columns ${group.fromCol}..${group.toCol}; use an ordered range within 0..${layout.colXs.length - 1}.`);
|
|
202
|
+
}
|
|
203
|
+
const contained = [...nodes.values()].some((node) => node.lane === group.lane && node.col >= group.fromCol && node.col <= group.toCol);
|
|
204
|
+
if (!contained) {
|
|
205
|
+
problems.push(`Group "${group.id}" does not contain any nodes - align its lane/columns with the parallel or branch work it frames.`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const byLane = new Map();
|
|
210
|
+
for (const node of nodes.values()) {
|
|
211
|
+
byLane.set(node.lane, [...(byLane.get(node.lane) || []), node]);
|
|
212
|
+
}
|
|
213
|
+
for (const [lane, laneNodes] of byLane) {
|
|
214
|
+
for (let i = 0; i < laneNodes.length; i += 1) {
|
|
215
|
+
for (let j = i + 1; j < laneNodes.length; j += 1) {
|
|
216
|
+
if (rectsOverlap(laneNodes[i], laneNodes[j], 8)) {
|
|
217
|
+
problems.push(`Nodes "${laneNodes[i].id}" and "${laneNodes[j].id}" are less than 8px apart in lane "${lane}" - move one to another col, adjust yOffset, or reduce width/height.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
for (const edge of workflow.edges) {
|
|
224
|
+
if (!nodes.has(edge.from)) problems.push(`Edge "${edge.label || edge.from}" references unknown source "${edge.from}".`);
|
|
225
|
+
if (!nodes.has(edge.to)) problems.push(`Edge "${edge.label || edge.to}" references unknown target "${edge.to}".`);
|
|
226
|
+
if (nodes.has(edge.from) && nodes.has(edge.to)) {
|
|
227
|
+
const routed = pathFor(edge);
|
|
228
|
+
if (routed.points.length === 2) {
|
|
229
|
+
const [start, end] = routed.points;
|
|
230
|
+
const segmentLength = Math.hypot(end[0] - start[0], end[1] - start[1]);
|
|
231
|
+
if (segmentLength < 28) {
|
|
232
|
+
problems.push(`Edge "${edge.from}" -> "${edge.to}" is too short (${Math.round(segmentLength)}px; minimum 28px) - drop its label or route it through a channel.`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const segments = [];
|
|
236
|
+
for (let i = 1; i < routed.points.length; i += 1) {
|
|
237
|
+
segments.push({ start: routed.points[i - 1], end: routed.points[i] });
|
|
238
|
+
}
|
|
239
|
+
for (const node of nodes.values()) {
|
|
240
|
+
if (node.id === edge.from || node.id === edge.to) continue;
|
|
241
|
+
if (segments.some((segment) => segmentIntersectsRect(segment, node, 2))) {
|
|
242
|
+
problems.push(`Edge "${edge.from}" -> "${edge.to}" crosses node "${node.id}" - adjust fromSide/toSide, route it through a channel, or move one node to a clearer lane/column.`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (Array.isArray(workflow.mainPath)) {
|
|
249
|
+
for (const id of workflow.mainPath) {
|
|
250
|
+
if (!nodes.has(id)) {
|
|
251
|
+
problems.push(`mainPath references unknown node "${id}".`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
for (let i = 0; i < workflow.mainPath.length - 1; i += 1) {
|
|
255
|
+
const fromId = workflow.mainPath[i];
|
|
256
|
+
const toId = workflow.mainPath[i + 1];
|
|
257
|
+
const from = nodes.get(fromId);
|
|
258
|
+
const to = nodes.get(toId);
|
|
259
|
+
if (!from || !to) continue;
|
|
260
|
+
const linked = workflow.edges.some((edge) => edge.from === fromId && edge.to === toId);
|
|
261
|
+
if (!linked) {
|
|
262
|
+
problems.push(`mainPath step "${fromId}" -> "${toId}" has no matching edge - add the edge or remove the pair from mainPath.`);
|
|
263
|
+
}
|
|
264
|
+
if (to.col < from.col) {
|
|
265
|
+
problems.push(`mainPath step "${fromId}" -> "${toId}" moves backward from col ${from.col} to ${to.col} - use a return edge outside mainPath for loops.`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const labelRects = [];
|
|
271
|
+
for (const edge of workflow.edges) {
|
|
272
|
+
if (!edge.label || !nodes.has(edge.from) || !nodes.has(edge.to)) continue;
|
|
273
|
+
const [lx, ly] = labelPoint(edge, pathFor(edge).points);
|
|
274
|
+
const width = Math.max(30, textUnits(edge.label) * 4.8 + 10);
|
|
275
|
+
labelRects.push({ label: edge.label, x: lx - width / 2, y: ly - 10, width, height: 14, lx, ly });
|
|
276
|
+
}
|
|
277
|
+
for (const rect of labelRects) {
|
|
278
|
+
for (const node of nodes.values()) {
|
|
279
|
+
if (rectsOverlap(rect, node, -2)) {
|
|
280
|
+
problems.push(`Label "${rect.label}" overlaps node "${node.id}" - adjust labelDx/labelDy/labelSegment or set labelAt.\n${suggestLabelObstacleFix(rect, rect.lx, rect.ly, node, 'node')}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
for (let i = 0; i < labelRects.length; i += 1) {
|
|
285
|
+
for (let j = i + 1; j < labelRects.length; j += 1) {
|
|
286
|
+
if (rectsOverlap(labelRects[i], labelRects[j], -2)) {
|
|
287
|
+
problems.push(`Labels "${labelRects[i].label}" and "${labelRects[j].label}" overlap - adjust labelDx/labelDy or remove one label.\n${suggestLabelPairFix(labelRects[i], labelRects[j])}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (viewBox[0] < layout.laneX + layout.laneW + 16) {
|
|
293
|
+
problems.push(`viewBox width ${viewBox[0]} clips the ${layout.laneW}px lanes - set meta.viewBox[0] to at least ${layout.laneX + layout.laneW + 16}.`);
|
|
294
|
+
}
|
|
295
|
+
if (legendY() + 18 > viewBox[1]) {
|
|
296
|
+
problems.push(`Legend exceeds viewBox height ${viewBox[1]} - set meta.viewBox[1] to at least ${legendY() + 18}.`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (problems.length) {
|
|
300
|
+
throw new Error(`Workflow layout validation failed:\n- ${problems.join('\n- ')}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function gapYBetween(fromLane, toLane, bias = 0.5) {
|
|
305
|
+
const a = laneTop(fromLane) + layout.laneH;
|
|
306
|
+
const b = laneTop(toLane);
|
|
307
|
+
return a + (b - a) * bias;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function spanForCols(fromCol, toCol, pad = 46) {
|
|
311
|
+
const start = layout.colXs[fromCol] - pad;
|
|
312
|
+
const end = layout.colXs[toCol] + pad;
|
|
313
|
+
return { x: start, width: end - start, cx: (start + end) / 2 };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function sameLaneAutoVia(start, end) {
|
|
317
|
+
if (start[0] === end[0] || start[1] === end[1]) return [];
|
|
318
|
+
const midX = (start[0] + end[0]) / 2;
|
|
319
|
+
return [[midX, start[1]], [midX, end[1]]];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function routeVia(edge, from, to, start, end) {
|
|
323
|
+
if (edge.via) return edge.via;
|
|
324
|
+
switch (edge.route || 'auto') {
|
|
325
|
+
case 'straight':
|
|
326
|
+
return [];
|
|
327
|
+
case 'drop': {
|
|
328
|
+
const y = gapYBetween(from.lane, to.lane, edge.bias ?? 0.5);
|
|
329
|
+
return [[start[0], y], [end[0], y]];
|
|
330
|
+
}
|
|
331
|
+
case 'outside-right': {
|
|
332
|
+
const x = edge.channelX ?? layout.laneX + layout.laneW + 12;
|
|
333
|
+
return [[x, start[1]], [x, end[1]]];
|
|
334
|
+
}
|
|
335
|
+
case 'return-left': {
|
|
336
|
+
const x = edge.channelX ?? Math.min(from.x, to.x) - 28;
|
|
337
|
+
return [[x, start[1]], [x, end[1]]];
|
|
338
|
+
}
|
|
339
|
+
case 'bottom-channel': {
|
|
340
|
+
const y = edge.channelY ?? Math.max(from.y + from.height, to.y + to.height) + 32;
|
|
341
|
+
return [[start[0], y], [end[0], y]];
|
|
342
|
+
}
|
|
343
|
+
case 'up-channel': {
|
|
344
|
+
const y = edge.channelY ?? Math.min(from.y, to.y) - 28;
|
|
345
|
+
return [[start[0], y], [end[0], y]];
|
|
346
|
+
}
|
|
347
|
+
case 'auto':
|
|
348
|
+
default: {
|
|
349
|
+
if (from.lane === to.lane) return sameLaneAutoVia(start, end);
|
|
350
|
+
const y = gapYBetween(from.lane, to.lane, edge.bias ?? 0.5);
|
|
351
|
+
return [[start[0], y], [end[0], y]];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const pathCache = new Map();
|
|
357
|
+
|
|
358
|
+
function pathFor(edge) {
|
|
359
|
+
if (pathCache.has(edge)) return pathCache.get(edge);
|
|
360
|
+
const from = nodes.get(edge.from);
|
|
361
|
+
const to = nodes.get(edge.to);
|
|
362
|
+
const start = anchor(from, chosenSide(edge.fromSide, defaultFromSide(from, to)));
|
|
363
|
+
const end = anchor(to, chosenSide(edge.toSide, defaultToSide(from, to)));
|
|
364
|
+
const points = [start, ...routeVia(edge, from, to, start, end), end];
|
|
365
|
+
const routed = { d: polylinePath(points), points };
|
|
366
|
+
pathCache.set(edge, routed);
|
|
367
|
+
return routed;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function renderLane(lane, index) {
|
|
371
|
+
const y = layout.laneY + index * (layout.laneH + layout.laneGap);
|
|
372
|
+
const exception = lane.variant === 'exception'
|
|
373
|
+
? `\n <rect x="${layout.laneX + 6}" y="${y + 6}" width="${layout.laneW - 12}" height="${layout.laneH - 12}" rx="8" class="c-security-group" stroke-width="1"/>`
|
|
374
|
+
: '';
|
|
375
|
+
const labelClass = lane.variant === 'exception' ? 't-security' : 't-dim';
|
|
376
|
+
const prefix = lane.variant === 'exception' ? 'EX' : String(index + 1).padStart(2, '0');
|
|
377
|
+
return ` <rect x="${layout.laneX}" y="${y}" width="${layout.laneW}" height="${layout.laneH}" rx="10" class="c-lane" stroke-width="1"/>${exception}
|
|
378
|
+
<text x="${layout.laneX + 14}" y="${y + 22}" class="${labelClass}" font-size="10" font-weight="600">${prefix} / ${esc(lane.label)}</text>`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function renderPhase(phase) {
|
|
382
|
+
const span = spanForCols(phase.fromCol, phase.toCol, 46);
|
|
383
|
+
const accent = variantAccent(phase.variant);
|
|
384
|
+
const [lineClass] = arrowClassMap[phase.variant || 'default'] || arrowClassMap.default;
|
|
385
|
+
return ` <line x1="${span.x}" y1="35" x2="${span.x + span.width}" y2="35" class="${lineClass}" stroke-width="1.1"/>
|
|
386
|
+
<rect x="${span.x}" y="27" width="${span.width}" height="16" rx="4" class="c-mask"/>
|
|
387
|
+
<text x="${span.cx}" y="39" class="${accent}" font-size="8" font-weight="600" text-anchor="middle">${esc(phase.label)}</text>`;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function renderGroup(group) {
|
|
391
|
+
const span = spanForCols(group.fromCol, group.toCol, 50);
|
|
392
|
+
const y = laneTop(group.lane) + layout.laneTitleH + 8;
|
|
393
|
+
const height = layout.laneH - layout.laneTitleH - 16;
|
|
394
|
+
const cls = group.variant === 'security' ? 'c-security-group' : 'c-lane';
|
|
395
|
+
const textClass = variantAccent(group.variant);
|
|
396
|
+
return ` <rect x="${span.x}" y="${y}" width="${span.width}" height="${height}" rx="9" class="${cls}" stroke-width="1"/>
|
|
397
|
+
<text x="${span.x + 10}" y="${y + 14}" class="${textClass}" font-size="7" font-weight="600">${esc(group.label)}</text>`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function renderNode(node) {
|
|
401
|
+
const fill = componentFill[node.type] || 'c-external';
|
|
402
|
+
const accent = componentText[node.type] || 't-muted';
|
|
403
|
+
const tag = node.tag
|
|
404
|
+
? `\n <text x="${node.cx}" y="${node.y + node.height - 12}" class="${accent}" font-size="7" text-anchor="middle">${esc(node.tag)}</text>`
|
|
405
|
+
: '';
|
|
406
|
+
return ` <rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="6" class="c-mask"/>
|
|
407
|
+
<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="6" class="${fill}"${animateAttr(workflow.meta, 'node', nodeStep(node))} stroke-width="1.5"/>
|
|
408
|
+
<text x="${node.cx}" y="${node.y + 21}" class="t-primary" font-size="11" font-weight="600" text-anchor="middle">${esc(node.label)}</text>
|
|
409
|
+
<text x="${node.cx}" y="${node.y + 38}" class="t-muted" font-size="8" text-anchor="middle">${esc(node.sublabel || '')}</text>${tag}`;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function renderEdgePath(edge) {
|
|
413
|
+
const [cls, marker] = arrowClassMap[edge.variant || 'default'] || arrowClassMap.default;
|
|
414
|
+
const routed = pathFor(edge);
|
|
415
|
+
const strokeWidth = edge.width || (edge.variant === 'emphasis' ? 1.8 : 1.4);
|
|
416
|
+
return ` <path d="${routed.d}" class="${cls}"${animateAttr(workflow.meta, 'edge', edgeSteps.get(edge))} stroke-width="${strokeWidth}" marker-end="url(#${marker})"/>`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function renderEdgeLabel(edge) {
|
|
420
|
+
if (!edge.label) return '';
|
|
421
|
+
const routed = pathFor(edge);
|
|
422
|
+
const [lx, ly] = labelPoint(edge, routed.points);
|
|
423
|
+
const labelW = Math.max(30, textUnits(edge.label) * 4.8 + 10);
|
|
424
|
+
return ` <rect x="${lx - labelW / 2}" y="${ly - 10}" width="${labelW}" height="14" rx="3" class="c-mask"/>
|
|
425
|
+
<text x="${lx}" y="${ly}" class="${variantAccent(edge.variant, { dashed: 't-database' })}" font-size="8" text-anchor="middle">${esc(edge.label)}</text>`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function renderLegend() {
|
|
429
|
+
const y = legendY();
|
|
430
|
+
return ` <text x="175" y="${y - 20}" class="t-primary" font-size="10" font-weight="600">Legend</text>
|
|
431
|
+
<rect x="175" y="${y - 8}" width="14" height="9" rx="2" class="c-frontend" stroke-width="1"/>
|
|
432
|
+
<text x="195" y="${y}" class="t-muted" font-size="7">User UI</text>
|
|
433
|
+
<rect x="260" y="${y - 8}" width="14" height="9" rx="2" class="c-backend" stroke-width="1"/>
|
|
434
|
+
<text x="280" y="${y}" class="t-muted" font-size="7">Agent logic</text>
|
|
435
|
+
<rect x="370" y="${y - 8}" width="14" height="9" rx="2" class="c-security" stroke-width="1"/>
|
|
436
|
+
<text x="390" y="${y}" class="t-muted" font-size="7">Policy</text>
|
|
437
|
+
<rect x="455" y="${y - 8}" width="14" height="9" rx="2" class="c-messagebus" stroke-width="1"/>
|
|
438
|
+
<text x="475" y="${y}" class="t-muted" font-size="7">Tool action</text>
|
|
439
|
+
<rect x="565" y="${y - 8}" width="14" height="9" rx="2" class="c-database" stroke-width="1"/>
|
|
440
|
+
<text x="585" y="${y}" class="t-muted" font-size="7">Context / trace</text>`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function renderSvg() {
|
|
444
|
+
return ` <svg viewBox="0 0 ${viewBox[0]} ${viewBox[1]}" ${svgRootAttrs(workflow.meta, 'workflow diagram')}>
|
|
445
|
+
${renderDefinitions()}
|
|
446
|
+
|
|
447
|
+
<!-- Background Grid -->
|
|
448
|
+
<rect width="100%" height="100%" fill="url(#grid)" />
|
|
449
|
+
|
|
450
|
+
<!-- Swimlanes -->
|
|
451
|
+
${workflow.lanes.map(renderLane).join('\n\n')}
|
|
452
|
+
|
|
453
|
+
<!-- Phase headers -->
|
|
454
|
+
${asArray(workflow.phases).map(renderPhase).join('\n')}
|
|
455
|
+
|
|
456
|
+
<!-- Workflow groups -->
|
|
457
|
+
${asArray(workflow.groups).map(renderGroup).join('\n')}
|
|
458
|
+
|
|
459
|
+
<!-- Edge paths -->
|
|
460
|
+
${workflow.edges.map(renderEdgePath).join('\n')}
|
|
461
|
+
|
|
462
|
+
<!-- Nodes -->
|
|
463
|
+
${[...nodes.values()].map(renderNode).join('\n\n')}
|
|
464
|
+
|
|
465
|
+
<!-- Edge labels -->
|
|
466
|
+
${workflow.edges.map(renderEdgeLabel).join('\n')}
|
|
467
|
+
|
|
468
|
+
<!-- Legend -->
|
|
469
|
+
${renderLegend()}
|
|
470
|
+
</svg>`;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
validateWorkflow();
|
|
474
|
+
writeDiagram({
|
|
475
|
+
outPath,
|
|
476
|
+
template,
|
|
477
|
+
meta: workflow.meta,
|
|
478
|
+
footerLabel: 'Workflow diagram',
|
|
479
|
+
svg: renderSvg(),
|
|
480
|
+
cards: workflow.cards,
|
|
481
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Technical Diagram JSON IR Schemas
|
|
2
|
+
|
|
3
|
+
Each typed renderer consumes a JSON intermediate representation (IR) validated
|
|
4
|
+
against one of the schemas in this folder before any layout work happens.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
| Schema | Governs | Structural arrays |
|
|
9
|
+
|--------|---------|-------------------|
|
|
10
|
+
| `workflow.schema.json` | `diagram_type: "workflow"` | `lanes`, `phases`, `groups`, `mainPath`, `nodes`, `edges` |
|
|
11
|
+
| `sequence.schema.json` | `diagram_type: "sequence"` | `participants`, `segments`, `messages`, `activations` |
|
|
12
|
+
| `dataflow.schema.json` | `diagram_type: "dataflow"` | `stages`, `nodes`, `flows` |
|
|
13
|
+
| `lifecycle.schema.json` | `diagram_type: "lifecycle"` | `lanes`, `states`, `transitions` |
|
|
14
|
+
| `architecture.schema.json` | `diagram_type: "architecture"` | `components`, `boundaries`, `connections` |
|
|
15
|
+
| `common.schema.json` | shared `$defs` only (no top-level document) | - |
|
|
16
|
+
|
|
17
|
+
Every diagram schema requires `schema_version`, `diagram_type`, `meta` (with
|
|
18
|
+
`title`), and its structural arrays - except `segments`, `activations`, and
|
|
19
|
+
`cards`, which are optional - and sets `additionalProperties: false` at every
|
|
20
|
+
level, so unknown fields are rejected rather than silently ignored.
|
|
21
|
+
|
|
22
|
+
Every `meta` object also accepts `animation: "trace"` for opt-in SVG/CSS motion
|
|
23
|
+
in generated HTML. Omit it, or set `"none"`, for the default static output.
|
|
24
|
+
|
|
25
|
+
## schema_version policy
|
|
26
|
+
|
|
27
|
+
`schema_version` is `"const": 1`. The constant pins the IR contract: a file
|
|
28
|
+
that validates today keeps rendering identically on every 2.x release. A
|
|
29
|
+
breaking change to any IR shape bumps the constant to `2`; renderers will then
|
|
30
|
+
reject version-1 files with a clear schema error instead of misrendering them.
|
|
31
|
+
Additive, backwards-compatible fields do not bump the version.
|
|
32
|
+
|
|
33
|
+
## Shared definitions (common.schema.json)
|
|
34
|
+
|
|
35
|
+
The five diagram schemas reference `common.schema.json#/$defs/...`:
|
|
36
|
+
|
|
37
|
+
- `id` - element identifiers, pattern `^[a-zA-Z][a-zA-Z0-9_-]*$`
|
|
38
|
+
- `point` - an `[x, y]` pair of numbers (used by `via` and `labelAt`)
|
|
39
|
+
- `componentType` - `frontend`, `backend`, `database`, `cloud`, `security`,
|
|
40
|
+
`messagebus`, `external`
|
|
41
|
+
- `variant` - `default`, `emphasis`, `security`, `dashed` (sequence messages
|
|
42
|
+
extend this list locally with `return`)
|
|
43
|
+
- `cards` - the summary-card blocks rendered below the SVG
|
|
44
|
+
|
|
45
|
+
Lifecycle state `type` is mode-specific (`start`/`active`/`waiting`/...) and
|
|
46
|
+
stays in `lifecycle.schema.json`.
|
|
47
|
+
|
|
48
|
+
## Runtime validation
|
|
49
|
+
|
|
50
|
+
`renderers/shared/generated-validators.mjs` contains the precompiled
|
|
51
|
+
standalone validators from the credited upstream source. It has no imports,
|
|
52
|
+
package dependencies, or network dependency. Do not add a schema compiler to
|
|
53
|
+
this skill. `renderers/shared/validator.mjs` applies the matching standalone
|
|
54
|
+
validator before the renderer's own layout checks.
|
|
55
|
+
|
|
56
|
+
## Error format
|
|
57
|
+
|
|
58
|
+
Schema violations exit non-zero. Each validation error is reported on its own line as
|
|
59
|
+
the instance path - annotated with the nearest enclosing element's `id` or
|
|
60
|
+
`label` - followed by the message and parameters:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
workflow schema validation failed:
|
|
64
|
+
/nodes/3 (id/label: "router") must NOT have additional properties {"additionalProperty":"colour"}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Schemas catch shape errors (types, enums, ranges, unknown fields); geometry
|
|
68
|
+
problems such as overlaps and label collisions are the renderers' job.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/tt-a1i/archify/schemas/architecture.schema.json",
|
|
4
|
+
"title": "Technical Architecture Diagram",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "diagram_type", "meta", "components"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"diagram_type": { "const": "architecture" },
|
|
11
|
+
"meta": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"required": ["title"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"title": { "type": "string", "minLength": 1 },
|
|
17
|
+
"subtitle": { "type": "string" },
|
|
18
|
+
"output": { "type": "string" },
|
|
19
|
+
"animation": { "enum": ["trace", "none"] },
|
|
20
|
+
"viewBox": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"prefixItems": [
|
|
23
|
+
{ "type": "number", "minimum": 320 },
|
|
24
|
+
{ "type": "number", "minimum": 240 }
|
|
25
|
+
],
|
|
26
|
+
"items": false,
|
|
27
|
+
"minItems": 2,
|
|
28
|
+
"maxItems": 2
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"layout": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"required": ["mode"],
|
|
36
|
+
"properties": {
|
|
37
|
+
"mode": { "enum": ["grid"] },
|
|
38
|
+
"origin": { "$ref": "common.schema.json#/$defs/point" },
|
|
39
|
+
"cols": { "type": "integer", "minimum": 1, "maximum": 12 },
|
|
40
|
+
"gapX": { "type": "number", "minimum": 0 },
|
|
41
|
+
"gapY": { "type": "number", "minimum": 0 },
|
|
42
|
+
"cellW": { "type": "number", "minimum": 40 },
|
|
43
|
+
"cellH": { "type": "number", "minimum": 24 }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"components": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"minItems": 1,
|
|
49
|
+
"items": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"required": ["id", "type", "label"],
|
|
53
|
+
"properties": {
|
|
54
|
+
"id": { "$ref": "common.schema.json#/$defs/id" },
|
|
55
|
+
"type": { "$ref": "common.schema.json#/$defs/componentType" },
|
|
56
|
+
"label": { "type": "string", "minLength": 1 },
|
|
57
|
+
"sublabel": { "type": "string" },
|
|
58
|
+
"tag": { "type": "string" },
|
|
59
|
+
"row": { "type": "integer", "minimum": 0 },
|
|
60
|
+
"col": { "type": "integer", "minimum": 0 },
|
|
61
|
+
"pos": { "$ref": "common.schema.json#/$defs/point" },
|
|
62
|
+
"size": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"prefixItems": [
|
|
65
|
+
{ "type": "number", "exclusiveMinimum": 0 },
|
|
66
|
+
{ "type": "number", "exclusiveMinimum": 0 }
|
|
67
|
+
],
|
|
68
|
+
"items": false,
|
|
69
|
+
"minItems": 2,
|
|
70
|
+
"maxItems": 2
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"boundaries": {
|
|
76
|
+
"type": "array",
|
|
77
|
+
"items": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"additionalProperties": false,
|
|
80
|
+
"required": ["kind", "label", "wraps"],
|
|
81
|
+
"properties": {
|
|
82
|
+
"kind": { "enum": ["region", "security-group"] },
|
|
83
|
+
"label": { "type": "string", "minLength": 1 },
|
|
84
|
+
"wraps": {
|
|
85
|
+
"type": "array",
|
|
86
|
+
"minItems": 1,
|
|
87
|
+
"items": { "$ref": "common.schema.json#/$defs/id" }
|
|
88
|
+
},
|
|
89
|
+
"pad": { "type": "number", "minimum": 0 }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"connections": {
|
|
94
|
+
"type": "array",
|
|
95
|
+
"items": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"additionalProperties": false,
|
|
98
|
+
"required": ["from", "to"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"from": { "$ref": "common.schema.json#/$defs/id" },
|
|
101
|
+
"to": { "$ref": "common.schema.json#/$defs/id" },
|
|
102
|
+
"label": { "type": "string" },
|
|
103
|
+
"variant": { "$ref": "common.schema.json#/$defs/variant" },
|
|
104
|
+
"fromSide": { "enum": ["left", "right", "top", "bottom"] },
|
|
105
|
+
"toSide": { "enum": ["left", "right", "top", "bottom"] },
|
|
106
|
+
"route": { "enum": ["auto", "straight", "orthogonal-h", "orthogonal-v"] },
|
|
107
|
+
"via": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"items": { "$ref": "common.schema.json#/$defs/point" }
|
|
110
|
+
},
|
|
111
|
+
"labelAt": { "$ref": "common.schema.json#/$defs/point" },
|
|
112
|
+
"labelDx": { "type": "number" },
|
|
113
|
+
"labelDy": { "type": "number" },
|
|
114
|
+
"labelSegment": { "type": "integer", "minimum": 0 },
|
|
115
|
+
"width": { "type": "number", "minimum": 0.5 }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"cards": { "$ref": "common.schema.json#/$defs/cards" }
|
|
120
|
+
}
|
|
121
|
+
}
|