create-yss-spec 3.5.2 → 3.5.4
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/package.json +1 -1
- package/src/template/asset-runtime.js +23 -6
- package/template/.agents/skills/.strategic-design-skills-manifest.json +5 -5
- package/template/.agents/skills/archify/SKILL.md +4 -52
- package/template/.agents/skills/archify/references/authoring-notes.md +12 -0
- package/template/.agents/skills/archify/references/authoring-workflow.md +25 -0
- package/template/.agents/skills/archify/references/delivery-workflow.md +25 -0
- package/template/.agents/skills/archify/references/upstream-updates.md +12 -0
- package/template/.agents/skills/code-review/SKILL.md +3 -34
- package/template/.agents/skills/code-review/references/legacy-convergence.md +5 -0
- package/template/.agents/skills/code-review/references/review-axis-checklists.md +18 -0
- package/template/.agents/skills/code-review/references/smell-baseline.md +23 -0
- package/template/.agents/skills/codebase-design/SKILL.md +4 -0
- package/template/.agents/skills/codebase-design/references/architecture-audit.md +69 -0
- package/template/.agents/skills/diagnosing-bugs/SKILL.md +3 -91
- package/template/.agents/skills/diagnosing-bugs/references/feedback-loop.md +53 -0
- package/template/.agents/skills/diagnosing-bugs/references/investigation.md +49 -0
- package/template/.agents/skills/frontend-commit/SKILL.md +4 -93
- package/template/.agents/skills/git-commit-core/SKILL.md +18 -0
- package/template/.agents/skills/git-commit-core/agents/openai.yaml +2 -0
- package/template/.agents/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.agents/skills/llm-wiki/SKILL.md +12 -13
- package/template/.agents/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.agents/skills/llm-wiki/references/compile.md +21 -114
- package/template/.agents/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.agents/skills/llm-wiki/references/lint.md +9 -46
- package/template/.agents/skills/llm-wiki/references/query.md +9 -18
- package/template/.agents/skills/llm-wiki/references/schema.md +56 -29
- package/template/.agents/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.agents/skills/llm-wiki/references/writing.md +4 -4
- package/template/.agents/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.agents/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.agents/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.agents/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.agents/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.agents/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.agents/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.agents/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.agents/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.agents/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.agents/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.agents/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.agents/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.agents/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.agents/skills/maintaining-skills/references/authoring.md +6 -0
- package/template/.agents/skills/prototype/LOGIC.md +1 -1
- package/template/.agents/skills/prototype/SKILL.md +5 -19
- package/template/.agents/skills/prototype-review/SKILL.md +11 -0
- package/template/.agents/skills/prototype-review/references/component-qa.md +32 -0
- package/template/.agents/skills/setup-matt-pocock-skills/SKILL.md +4 -74
- package/template/.agents/skills/setup-matt-pocock-skills/references/configuration-decisions.md +42 -0
- package/template/.agents/skills/setup-matt-pocock-skills/references/write-configuration.md +42 -0
- package/template/.agents/skills/to-spec/SKILL.md +9 -62
- package/template/.agents/skills/to-tickets/SKILL.md +20 -86
- package/template/.agents/skills/using-git-worktrees/SKILL.md +5 -122
- package/template/.agents/skills/using-git-worktrees/references/git-fallback.md +46 -0
- package/template/.agents/skills/using-git-worktrees/references/setup-and-baseline.md +45 -0
- package/template/.agents/skills/using-git-worktrees/references/troubleshooting.md +47 -0
- package/template/.agents/skills/wayfinder/SKILL.md +7 -86
- package/template/.agents/skills/wayfinder/references/chart-map.md +14 -0
- package/template/.agents/skills/wayfinder/references/map-format.md +49 -0
- package/template/.agents/skills/wayfinder/references/scope-and-frontier.md +24 -0
- package/template/.agents/skills/wayfinder/references/ticket-types.md +12 -0
- package/template/.agents/skills/wayfinder/references/work-map.md +15 -0
- package/template/.agents/skills/writing-for-agents/SKILL-MECHANICS.md +20 -11
- package/template/.agents/skills/yedit-table-usage/SKILL.md +5 -172
- package/template/.agents/skills/yedit-table-usage/references/editable-table-example.md +141 -0
- package/template/.agents/skills/yedit-table-usage/references/select-and-filter-examples.md +38 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +4 -25
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/engineering-checks.md +11 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/generation-workflow.md +14 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/platform-selection.md +16 -0
- package/template/.agents/skills/yss-design-system/SKILL.md +13 -3
- package/template/.agents/skills/yss-design-system/assets/enterprise-craft/app.js +53 -0
- package/template/.agents/skills/yss-design-system/assets/enterprise-craft/index.html +2 -0
- package/template/.agents/skills/yss-design-system/assets/enterprise-craft/scenarios.js +14 -0
- package/template/.agents/skills/yss-design-system/assets/enterprise-craft/styles.css +32 -0
- package/template/.agents/skills/yss-design-system/references/data-quality-theme.md +2 -2
- package/template/.agents/skills/yss-design-system/references/design-qa-theme.md +1 -1
- package/template/.agents/skills/yss-design-system/references/design-system.md +3 -3
- package/template/.agents/skills/yss-design-system/references/enterprise-craft.md +33 -0
- package/template/.agents/skills/yss-design-system/references/page-composition.md +24 -0
- package/template/.agents/skills/yss-design-system/references/prototype-default-theme.md +26 -0
- package/template/.agents/skills/yss-design-system/references/search-date-theme.md +19 -0
- package/template/.agents/skills/yss-design-system/scripts/export-craft-examples.mjs +30 -0
- package/template/.agents/skills/yss-hook/SKILL.md +8 -102
- package/template/.agents/skills/yss-hook/references/async-actions.md +49 -0
- package/template/.agents/skills/yss-hook/references/list-pagination.md +46 -0
- package/template/.agents/skills/yss-hook/references/polling.md +20 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +8 -3
- package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.agents/skills/yss-implementation-contract-compiler/references/scaffold-compilation.md +7 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +2 -49
- package/template/.agents/skills/yss-openapi-draft-review/references/review-output.md +45 -0
- package/template/.agents/skills/yss-openapi-draft-review/references/semantic-review.md +16 -0
- package/template/.agents/skills/yss-openapi-governance/SKILL.md +2 -48
- package/template/.agents/skills/yss-openapi-governance/references/governance-workflow.md +38 -0
- package/template/.agents/skills/yss-openapi-governance/references/locked-toolchain.md +22 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +24 -20
- package/template/.agents/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +429 -384
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.agents/skills/yss-product-lifecycle/references/plan-requirements.md +12 -0
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +3 -3
- package/template/.agents/skills/yss-prototype-stage/SKILL.md +27 -8
- package/template/.agents/skills/yss-prototype-stage/assets/comparison/comparison.css +12 -0
- package/template/.agents/skills/yss-prototype-stage/assets/comparison/comparison.js +52 -0
- package/template/.agents/skills/yss-prototype-stage/assets/comparison/index.html +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/comparison/review-ready.js +2 -0
- package/template/.agents/skills/yss-prototype-stage/assets/low-fidelity-exercise/app.js +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/low-fidelity-exercise/index.html +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/low-fidelity-exercise/styles.css +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/native-workbench/app.js +12 -6
- package/template/.agents/skills/yss-prototype-stage/assets/native-workbench/index.html +1 -1
- package/template/.agents/skills/yss-prototype-stage/assets/native-workbench/scenarios.json +297 -0
- package/template/.agents/skills/yss-prototype-stage/assets/native-workbench/styles.css +2 -0
- package/template/.agents/skills/yss-prototype-stage/assets/offline-html/app.js +7 -5
- package/template/.agents/skills/yss-prototype-stage/assets/offline-html/scenarios.json +297 -0
- package/template/.agents/skills/yss-prototype-stage/assets/prototype-theme.css +92 -0
- package/template/.agents/skills/yss-prototype-stage/assets/scenario-runtime.js +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/SHADCN-LICENSE.txt +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/package.json +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/pnpm-lock.yaml +1278 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/registry-manifest.json +936 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/theme.css +107 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/Alert.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertDescription.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertTitle.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/index.ts +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialog.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogAction.vue +18 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogCancel.vue +25 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogContent.vue +44 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogDescription.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogFooter.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogHeader.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTitle.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/index.ts +9 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/Badge.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/index.ts +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/Breadcrumb.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbList.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/index.ts +7 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/Button.vue +31 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/index.ts +40 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/Card.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardAction.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardContent.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardDescription.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardFooter.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardHeader.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardTitle.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/index.ts +7 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/Checkbox.vue +35 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/Combobox.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxAnchor.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxEmpty.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxGroup.vue +27 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxInput.vue +42 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItem.vue +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItemIndicator.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxList.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxSeparator.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxTrigger.vue +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxViewport.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/index.ts +13 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/Dialog.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogClose.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogContent.vue +53 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogDescription.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogFooter.vue +27 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogHeader.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogOverlay.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogScrollContent.vue +59 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTitle.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTrigger.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/index.ts +10 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/index.ts +16 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/Empty.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyContent.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyDescription.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyHeader.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyMedia.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyTitle.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/index.ts +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/Field.vue +25 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldContent.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldDescription.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldError.vue +53 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldGroup.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLabel.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLegend.vue +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSeparator.vue +29 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSet.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldTitle.vue +20 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/index.ts +39 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/Input.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroup.vue +35 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupAddon.vue +36 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupButton.vue +29 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupInput.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupText.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupTextarea.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/index.ts +51 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/Label.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/Pagination.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationContent.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationEllipsis.vue +25 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationFirst.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationItem.vue +34 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationLast.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationNext.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationPrevious.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/index.ts +8 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/Popover.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverAnchor.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverContent.vue +45 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverTrigger.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/index.ts +4 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendar.vue +62 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCell.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCellTrigger.vue +41 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGrid.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridBody.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridHead.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridRow.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeadCell.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeader.vue +23 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeading.vue +30 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarNextButton.vue +32 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarPrevButton.vue +32 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/index.ts +12 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/Select.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectContent.vue +51 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectGroup.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItem.vue +44 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItemText.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectLabel.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollDownButton.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollUpButton.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectSeparator.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectTrigger.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectValue.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/index.ts +11 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/Separator.vue +29 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/Sheet.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetClose.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetContent.vue +62 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetDescription.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetFooter.vue +16 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetHeader.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetOverlay.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTitle.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTrigger.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/index.ts +8 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/Skeleton.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/Spinner.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/Table.vue +16 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableBody.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCaption.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCell.vue +22 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableEmpty.vue +34 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableFooter.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHead.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHeader.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableRow.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/index.ts +9 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/Tabs.vue +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsContent.vue +21 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsList.vue +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsTrigger.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/index.ts +4 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/Textarea.vue +28 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/index.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/Tooltip.vue +19 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipContent.vue +34 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipProvider.vue +14 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipTrigger.vue +15 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/index.ts +4 -0
- package/template/.agents/skills/yss-prototype-stage/assets/shadcn-vue-authoring/utils.ts +7 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/Analysis.vue +16 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/Approval.vue +14 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/CombinedQuery.vue +17 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/ComponentStates.vue +41 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/Conflict.vue +33 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/DateRange.vue +29 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/Frame.vue +7 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/ListDetail.vue +47 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/MultiStep.vue +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/Notice.vue +5 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/OwnerValidity.vue +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/SearchSelect.vue +32 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/Workspace.vue +174 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/analysis.scenarios.json +276 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/approval.scenarios.json +207 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.css +14 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.js +24 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query.scenarios.json +298 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/conflict.scenarios.json +229 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail.scenarios.json +185 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/mount.ts +4 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step.scenarios.json +188 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity.scenarios.json +298 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/patterns.css +26 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/refinement.css +129 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workbench.ts +1 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-definition.ts +40 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-model.js +75 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-page.ts +18 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.config.json +6 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.ts +3 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.css +101 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.scenarios.json +2015 -0
- package/template/.agents/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.ts +26 -0
- package/template/.agents/skills/yss-prototype-stage/references/business-patterns.md +42 -0
- package/template/.agents/skills/yss-prototype-stage/references/comparison-tool.md +48 -0
- package/template/.agents/skills/yss-prototype-stage/references/component-recipes.md +41 -0
- package/template/.agents/skills/yss-prototype-stage/references/concept-comparison.md +26 -0
- package/template/.agents/skills/yss-prototype-stage/references/enterprise-workspace.md +53 -0
- package/template/.agents/skills/yss-prototype-stage/references/low-fidelity-exercise.md +32 -0
- package/template/.agents/skills/yss-prototype-stage/references/product-design-adapter.md +11 -5
- package/template/.agents/skills/yss-prototype-stage/references/prototype-profile-routing.md +10 -6
- package/template/.agents/skills/yss-prototype-stage/references/prototype-verification.md +25 -0
- package/template/.agents/skills/yss-prototype-stage/references/resource-lifecycle.md +20 -0
- package/template/.agents/skills/yss-prototype-stage/references/search-date-patterns.md +28 -0
- package/template/.agents/skills/yss-prototype-stage/references/shadcn-integration.md +5 -0
- package/template/.agents/skills/yss-prototype-stage/references/shadcn-vue-integration.md +46 -0
- package/template/.agents/skills/yss-prototype-stage/references/usability-research.md +26 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/browser-session.mjs +57 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/build-shadcn-vue-prototype.mjs +137 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/compose-workspace-scenarios.mjs +22 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/export-business-patterns.mjs +7 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/export-low-fidelity-exercise.mjs +18 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/export-vue-patterns.mjs +21 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/offline-html.mjs +60 -23
- package/template/.agents/skills/yss-prototype-stage/scripts/prototype-comparison.mjs +208 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/prototype-contract.mjs +19 -14
- package/template/.agents/skills/yss-prototype-stage/scripts/scenario-contract.mjs +31 -0
- package/template/.agents/skills/yss-prototype-stage/scripts/verify-prototype-design.mjs +97 -0
- package/template/.agents/skills/yss-prototype-stage/tests/antd-reference.test.mjs +4 -24
- package/template/.agents/skills/yss-prototype-stage/tests/browser-harness.mjs +37 -0
- package/template/.agents/skills/yss-prototype-stage/tests/browser-visual.mjs +22 -0
- package/template/.agents/skills/yss-prototype-stage/tests/business-controls-browser.mjs +43 -0
- package/template/.agents/skills/yss-prototype-stage/tests/business-controls.test.mjs +20 -0
- package/template/.agents/skills/yss-prototype-stage/tests/compact-theme-browser.mjs +53 -0
- package/template/.agents/skills/yss-prototype-stage/tests/comparison-browser-smoke.mjs +77 -0
- package/template/.agents/skills/yss-prototype-stage/tests/comparison-fixture.mjs +16 -0
- package/template/.agents/skills/yss-prototype-stage/tests/comparison.test.mjs +46 -0
- package/template/.agents/skills/yss-prototype-stage/tests/components-browser.mjs +60 -0
- package/template/.agents/skills/yss-prototype-stage/tests/patterns-browser.mjs +84 -0
- package/template/.agents/skills/yss-prototype-stage/tests/reliability.test.mjs +34 -0
- package/template/.agents/skills/yss-prototype-stage/tests/retirement.test.mjs +8 -0
- package/template/.agents/skills/yss-prototype-stage/tests/run-scenarios.mjs +19 -2
- package/template/.agents/skills/yss-prototype-stage/tests/shadcn-vue.test.mjs +43 -0
- package/template/.agents/skills/yss-prototype-stage/tests/verification-entry.test.mjs +16 -0
- package/template/.agents/skills/yss-prototype-stage/tests/workbench-smoke.mjs +36 -21
- package/template/.agents/skills/yss-prototype-stage/tests/workspace-browser.mjs +50 -0
- package/template/.agents/skills/yss-prototype-stage/tests/workspace.test.mjs +70 -0
- package/template/.agents/skills/yss-prototype-stage/tests/zoom-browser.mjs +26 -0
- package/template/.agents/skills/yss-research/SKILL.md +4 -0
- package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.agents/skills/yss-technical-design/SKILL.md +4 -0
- package/template/.agents/skills/yss-ui-business-page-generation/SKILL.md +4 -61
- package/template/.agents/skills/yss-ui-business-page-generation/references/page-composition-example.md +50 -0
- package/template/.agents/skills/yss-ui-business-page-generation/references/page-layout-example.md +19 -0
- package/template/.agents/skills/ytable-usage/SKILL.md +3 -191
- package/template/.agents/skills/ytable-usage/references/selection-and-batch.md +30 -0
- package/template/.agents/skills/ytable-usage/references/table-pagination-examples.md +170 -0
- package/template/.codex/skills/archify/SKILL.md +4 -52
- package/template/.codex/skills/archify/references/authoring-notes.md +12 -0
- package/template/.codex/skills/archify/references/authoring-workflow.md +25 -0
- package/template/.codex/skills/archify/references/delivery-workflow.md +25 -0
- package/template/.codex/skills/archify/references/upstream-updates.md +12 -0
- package/template/.codex/skills/code-review/SKILL.md +3 -34
- package/template/.codex/skills/code-review/references/legacy-convergence.md +5 -0
- package/template/.codex/skills/code-review/references/review-axis-checklists.md +18 -0
- package/template/.codex/skills/code-review/references/smell-baseline.md +23 -0
- package/template/.codex/skills/codebase-design/SKILL.md +4 -0
- package/template/.codex/skills/codebase-design/references/architecture-audit.md +69 -0
- package/template/.codex/skills/diagnosing-bugs/SKILL.md +3 -91
- package/template/.codex/skills/diagnosing-bugs/references/feedback-loop.md +53 -0
- package/template/.codex/skills/diagnosing-bugs/references/investigation.md +49 -0
- package/template/.codex/skills/frontend-commit/SKILL.md +4 -93
- package/template/.codex/skills/git-commit-core/SKILL.md +18 -0
- package/template/.codex/skills/git-commit-core/agents/openai.yaml +2 -0
- package/template/.codex/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.codex/skills/llm-wiki/SKILL.md +12 -13
- package/template/.codex/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.codex/skills/llm-wiki/references/compile.md +21 -114
- package/template/.codex/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.codex/skills/llm-wiki/references/lint.md +9 -46
- package/template/.codex/skills/llm-wiki/references/query.md +9 -18
- package/template/.codex/skills/llm-wiki/references/schema.md +56 -29
- package/template/.codex/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.codex/skills/llm-wiki/references/writing.md +4 -4
- package/template/.codex/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.codex/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.codex/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.codex/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.codex/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.codex/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.codex/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.codex/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.codex/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.codex/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.codex/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.codex/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.codex/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.codex/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.codex/skills/maintaining-skills/references/authoring.md +6 -0
- package/template/.codex/skills/prototype/LOGIC.md +1 -1
- package/template/.codex/skills/prototype/SKILL.md +5 -19
- package/template/.codex/skills/prototype-review/SKILL.md +11 -0
- package/template/.codex/skills/prototype-review/references/component-qa.md +32 -0
- package/template/.codex/skills/setup-matt-pocock-skills/SKILL.md +4 -74
- package/template/.codex/skills/setup-matt-pocock-skills/references/configuration-decisions.md +42 -0
- package/template/.codex/skills/setup-matt-pocock-skills/references/write-configuration.md +42 -0
- package/template/.codex/skills/to-spec/SKILL.md +9 -62
- package/template/.codex/skills/to-tickets/SKILL.md +20 -86
- package/template/.codex/skills/using-git-worktrees/SKILL.md +5 -122
- package/template/.codex/skills/using-git-worktrees/references/git-fallback.md +46 -0
- package/template/.codex/skills/using-git-worktrees/references/setup-and-baseline.md +45 -0
- package/template/.codex/skills/using-git-worktrees/references/troubleshooting.md +47 -0
- package/template/.codex/skills/wayfinder/SKILL.md +7 -86
- package/template/.codex/skills/wayfinder/references/chart-map.md +14 -0
- package/template/.codex/skills/wayfinder/references/map-format.md +49 -0
- package/template/.codex/skills/wayfinder/references/scope-and-frontier.md +24 -0
- package/template/.codex/skills/wayfinder/references/ticket-types.md +12 -0
- package/template/.codex/skills/wayfinder/references/work-map.md +15 -0
- package/template/.codex/skills/writing-for-agents/SKILL-MECHANICS.md +20 -11
- package/template/.codex/skills/yedit-table-usage/SKILL.md +5 -172
- package/template/.codex/skills/yedit-table-usage/references/editable-table-example.md +141 -0
- package/template/.codex/skills/yedit-table-usage/references/select-and-filter-examples.md +38 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +4 -25
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/engineering-checks.md +11 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/generation-workflow.md +14 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/platform-selection.md +16 -0
- package/template/.codex/skills/yss-design-system/SKILL.md +13 -3
- package/template/.codex/skills/yss-design-system/assets/enterprise-craft/app.js +53 -0
- package/template/.codex/skills/yss-design-system/assets/enterprise-craft/index.html +2 -0
- package/template/.codex/skills/yss-design-system/assets/enterprise-craft/scenarios.js +14 -0
- package/template/.codex/skills/yss-design-system/assets/enterprise-craft/styles.css +32 -0
- package/template/.codex/skills/yss-design-system/references/data-quality-theme.md +2 -2
- package/template/.codex/skills/yss-design-system/references/design-qa-theme.md +1 -1
- package/template/.codex/skills/yss-design-system/references/design-system.md +3 -3
- package/template/.codex/skills/yss-design-system/references/enterprise-craft.md +33 -0
- package/template/.codex/skills/yss-design-system/references/page-composition.md +24 -0
- package/template/.codex/skills/yss-design-system/references/prototype-default-theme.md +26 -0
- package/template/.codex/skills/yss-design-system/references/search-date-theme.md +19 -0
- package/template/.codex/skills/yss-design-system/scripts/export-craft-examples.mjs +30 -0
- package/template/.codex/skills/yss-hook/SKILL.md +8 -102
- package/template/.codex/skills/yss-hook/references/async-actions.md +49 -0
- package/template/.codex/skills/yss-hook/references/list-pagination.md +46 -0
- package/template/.codex/skills/yss-hook/references/polling.md +20 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +8 -3
- package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.codex/skills/yss-implementation-contract-compiler/references/scaffold-compilation.md +7 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +2 -49
- package/template/.codex/skills/yss-openapi-draft-review/references/review-output.md +45 -0
- package/template/.codex/skills/yss-openapi-draft-review/references/semantic-review.md +16 -0
- package/template/.codex/skills/yss-openapi-governance/SKILL.md +2 -48
- package/template/.codex/skills/yss-openapi-governance/references/governance-workflow.md +38 -0
- package/template/.codex/skills/yss-openapi-governance/references/locked-toolchain.md +22 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +24 -20
- package/template/.codex/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +429 -384
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.codex/skills/yss-product-lifecycle/references/plan-requirements.md +12 -0
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +3 -3
- package/template/.codex/skills/yss-prototype-stage/SKILL.md +27 -8
- package/template/.codex/skills/yss-prototype-stage/assets/comparison/comparison.css +12 -0
- package/template/.codex/skills/yss-prototype-stage/assets/comparison/comparison.js +52 -0
- package/template/.codex/skills/yss-prototype-stage/assets/comparison/index.html +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/comparison/review-ready.js +2 -0
- package/template/.codex/skills/yss-prototype-stage/assets/low-fidelity-exercise/app.js +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/low-fidelity-exercise/index.html +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/low-fidelity-exercise/styles.css +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/native-workbench/app.js +12 -6
- package/template/.codex/skills/yss-prototype-stage/assets/native-workbench/index.html +1 -1
- package/template/.codex/skills/yss-prototype-stage/assets/native-workbench/scenarios.json +297 -0
- package/template/.codex/skills/yss-prototype-stage/assets/native-workbench/styles.css +2 -0
- package/template/.codex/skills/yss-prototype-stage/assets/offline-html/app.js +7 -5
- package/template/.codex/skills/yss-prototype-stage/assets/offline-html/scenarios.json +297 -0
- package/template/.codex/skills/yss-prototype-stage/assets/prototype-theme.css +92 -0
- package/template/.codex/skills/yss-prototype-stage/assets/scenario-runtime.js +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/SHADCN-LICENSE.txt +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/package.json +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/pnpm-lock.yaml +1278 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/registry-manifest.json +936 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/theme.css +107 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/Alert.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertDescription.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertTitle.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/index.ts +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialog.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogAction.vue +18 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogCancel.vue +25 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogContent.vue +44 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogDescription.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogFooter.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogHeader.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTitle.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/index.ts +9 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/Badge.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/index.ts +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/Breadcrumb.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbList.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/index.ts +7 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/Button.vue +31 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/index.ts +40 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/Card.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardAction.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardContent.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardDescription.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardFooter.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardHeader.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardTitle.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/index.ts +7 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/Checkbox.vue +35 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/Combobox.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxAnchor.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxEmpty.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxGroup.vue +27 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxInput.vue +42 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItem.vue +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItemIndicator.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxList.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxSeparator.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxTrigger.vue +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxViewport.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/index.ts +13 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/Dialog.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogClose.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogContent.vue +53 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogDescription.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogFooter.vue +27 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogHeader.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogOverlay.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogScrollContent.vue +59 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTitle.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTrigger.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/index.ts +10 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/index.ts +16 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/Empty.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyContent.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyDescription.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyHeader.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyMedia.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyTitle.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/index.ts +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/Field.vue +25 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldContent.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldDescription.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldError.vue +53 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldGroup.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLabel.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLegend.vue +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSeparator.vue +29 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSet.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldTitle.vue +20 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/index.ts +39 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/Input.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroup.vue +35 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupAddon.vue +36 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupButton.vue +29 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupInput.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupText.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupTextarea.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/index.ts +51 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/Label.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/Pagination.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationContent.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationEllipsis.vue +25 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationFirst.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationItem.vue +34 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationLast.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationNext.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationPrevious.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/index.ts +8 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/Popover.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverAnchor.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverContent.vue +45 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverTrigger.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/index.ts +4 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendar.vue +62 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCell.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCellTrigger.vue +41 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGrid.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridBody.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridHead.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridRow.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeadCell.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeader.vue +23 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeading.vue +30 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarNextButton.vue +32 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarPrevButton.vue +32 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/index.ts +12 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/Select.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectContent.vue +51 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectGroup.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItem.vue +44 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItemText.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectLabel.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollDownButton.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollUpButton.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectSeparator.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectTrigger.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectValue.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/index.ts +11 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/Separator.vue +29 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/Sheet.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetClose.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetContent.vue +62 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetDescription.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetFooter.vue +16 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetHeader.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetOverlay.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTitle.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTrigger.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/index.ts +8 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/Skeleton.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/Spinner.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/Table.vue +16 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableBody.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCaption.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCell.vue +22 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableEmpty.vue +34 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableFooter.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHead.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHeader.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableRow.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/index.ts +9 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/Tabs.vue +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsContent.vue +21 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsList.vue +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsTrigger.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/index.ts +4 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/Textarea.vue +28 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/index.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/Tooltip.vue +19 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipContent.vue +34 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipProvider.vue +14 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipTrigger.vue +15 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/index.ts +4 -0
- package/template/.codex/skills/yss-prototype-stage/assets/shadcn-vue-authoring/utils.ts +7 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/Analysis.vue +16 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/Approval.vue +14 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/CombinedQuery.vue +17 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/ComponentStates.vue +41 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/Conflict.vue +33 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/DateRange.vue +29 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/Frame.vue +7 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/ListDetail.vue +47 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/MultiStep.vue +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/Notice.vue +5 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/OwnerValidity.vue +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/SearchSelect.vue +32 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/Workspace.vue +174 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/analysis.scenarios.json +276 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/approval.scenarios.json +207 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.css +14 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.js +24 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query.scenarios.json +298 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/conflict.scenarios.json +229 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail.scenarios.json +185 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/mount.ts +4 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step.scenarios.json +188 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity.scenarios.json +298 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/patterns.css +26 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/refinement.css +129 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workbench.ts +1 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-definition.ts +40 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-model.js +75 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-page.ts +18 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.config.json +6 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.ts +3 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.css +101 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.scenarios.json +2015 -0
- package/template/.codex/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.ts +26 -0
- package/template/.codex/skills/yss-prototype-stage/references/business-patterns.md +42 -0
- package/template/.codex/skills/yss-prototype-stage/references/comparison-tool.md +48 -0
- package/template/.codex/skills/yss-prototype-stage/references/component-recipes.md +41 -0
- package/template/.codex/skills/yss-prototype-stage/references/concept-comparison.md +26 -0
- package/template/.codex/skills/yss-prototype-stage/references/enterprise-workspace.md +53 -0
- package/template/.codex/skills/yss-prototype-stage/references/low-fidelity-exercise.md +32 -0
- package/template/.codex/skills/yss-prototype-stage/references/product-design-adapter.md +11 -5
- package/template/.codex/skills/yss-prototype-stage/references/prototype-profile-routing.md +10 -6
- package/template/.codex/skills/yss-prototype-stage/references/prototype-verification.md +25 -0
- package/template/.codex/skills/yss-prototype-stage/references/resource-lifecycle.md +20 -0
- package/template/.codex/skills/yss-prototype-stage/references/search-date-patterns.md +28 -0
- package/template/.codex/skills/yss-prototype-stage/references/shadcn-integration.md +5 -0
- package/template/.codex/skills/yss-prototype-stage/references/shadcn-vue-integration.md +46 -0
- package/template/.codex/skills/yss-prototype-stage/references/usability-research.md +26 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/browser-session.mjs +57 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/build-shadcn-vue-prototype.mjs +137 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/compose-workspace-scenarios.mjs +22 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/export-business-patterns.mjs +7 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/export-low-fidelity-exercise.mjs +18 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/export-vue-patterns.mjs +21 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/offline-html.mjs +60 -23
- package/template/.codex/skills/yss-prototype-stage/scripts/prototype-comparison.mjs +208 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/prototype-contract.mjs +19 -14
- package/template/.codex/skills/yss-prototype-stage/scripts/scenario-contract.mjs +31 -0
- package/template/.codex/skills/yss-prototype-stage/scripts/verify-prototype-design.mjs +97 -0
- package/template/.codex/skills/yss-prototype-stage/tests/antd-reference.test.mjs +4 -24
- package/template/.codex/skills/yss-prototype-stage/tests/browser-harness.mjs +37 -0
- package/template/.codex/skills/yss-prototype-stage/tests/browser-visual.mjs +22 -0
- package/template/.codex/skills/yss-prototype-stage/tests/business-controls-browser.mjs +43 -0
- package/template/.codex/skills/yss-prototype-stage/tests/business-controls.test.mjs +20 -0
- package/template/.codex/skills/yss-prototype-stage/tests/compact-theme-browser.mjs +53 -0
- package/template/.codex/skills/yss-prototype-stage/tests/comparison-browser-smoke.mjs +77 -0
- package/template/.codex/skills/yss-prototype-stage/tests/comparison-fixture.mjs +16 -0
- package/template/.codex/skills/yss-prototype-stage/tests/comparison.test.mjs +46 -0
- package/template/.codex/skills/yss-prototype-stage/tests/components-browser.mjs +60 -0
- package/template/.codex/skills/yss-prototype-stage/tests/patterns-browser.mjs +84 -0
- package/template/.codex/skills/yss-prototype-stage/tests/reliability.test.mjs +34 -0
- package/template/.codex/skills/yss-prototype-stage/tests/retirement.test.mjs +8 -0
- package/template/.codex/skills/yss-prototype-stage/tests/run-scenarios.mjs +19 -2
- package/template/.codex/skills/yss-prototype-stage/tests/shadcn-vue.test.mjs +43 -0
- package/template/.codex/skills/yss-prototype-stage/tests/verification-entry.test.mjs +16 -0
- package/template/.codex/skills/yss-prototype-stage/tests/workbench-smoke.mjs +36 -21
- package/template/.codex/skills/yss-prototype-stage/tests/workspace-browser.mjs +50 -0
- package/template/.codex/skills/yss-prototype-stage/tests/workspace.test.mjs +70 -0
- package/template/.codex/skills/yss-prototype-stage/tests/zoom-browser.mjs +26 -0
- package/template/.codex/skills/yss-research/SKILL.md +4 -0
- package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.codex/skills/yss-technical-design/SKILL.md +4 -0
- package/template/.codex/skills/yss-ui-business-page-generation/SKILL.md +4 -61
- package/template/.codex/skills/yss-ui-business-page-generation/references/page-composition-example.md +50 -0
- package/template/.codex/skills/yss-ui-business-page-generation/references/page-layout-example.md +19 -0
- package/template/.codex/skills/ytable-usage/SKILL.md +3 -191
- package/template/.codex/skills/ytable-usage/references/selection-and-batch.md +30 -0
- package/template/.codex/skills/ytable-usage/references/table-pagination-examples.md +170 -0
- package/template/.cursor/skills/archify/SKILL.md +4 -52
- package/template/.cursor/skills/archify/references/authoring-notes.md +12 -0
- package/template/.cursor/skills/archify/references/authoring-workflow.md +25 -0
- package/template/.cursor/skills/archify/references/delivery-workflow.md +25 -0
- package/template/.cursor/skills/archify/references/upstream-updates.md +12 -0
- package/template/.cursor/skills/code-review/SKILL.md +3 -34
- package/template/.cursor/skills/code-review/references/legacy-convergence.md +5 -0
- package/template/.cursor/skills/code-review/references/review-axis-checklists.md +18 -0
- package/template/.cursor/skills/code-review/references/smell-baseline.md +23 -0
- package/template/.cursor/skills/codebase-design/SKILL.md +4 -0
- package/template/.cursor/skills/codebase-design/references/architecture-audit.md +69 -0
- package/template/.cursor/skills/diagnosing-bugs/SKILL.md +3 -91
- package/template/.cursor/skills/diagnosing-bugs/references/feedback-loop.md +53 -0
- package/template/.cursor/skills/diagnosing-bugs/references/investigation.md +49 -0
- package/template/.cursor/skills/frontend-commit/SKILL.md +4 -93
- package/template/.cursor/skills/git-commit-core/SKILL.md +18 -0
- package/template/.cursor/skills/git-commit-core/agents/openai.yaml +2 -0
- package/template/.cursor/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.cursor/skills/llm-wiki/SKILL.md +12 -13
- package/template/.cursor/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.cursor/skills/llm-wiki/references/compile.md +21 -114
- package/template/.cursor/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.cursor/skills/llm-wiki/references/lint.md +9 -46
- package/template/.cursor/skills/llm-wiki/references/query.md +9 -18
- package/template/.cursor/skills/llm-wiki/references/schema.md +56 -29
- package/template/.cursor/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.cursor/skills/llm-wiki/references/writing.md +4 -4
- package/template/.cursor/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.cursor/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.cursor/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.cursor/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.cursor/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.cursor/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.cursor/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.cursor/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.cursor/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.cursor/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.cursor/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.cursor/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.cursor/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.cursor/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.cursor/skills/maintaining-skills/references/authoring.md +6 -0
- package/template/.cursor/skills/prototype/LOGIC.md +1 -1
- package/template/.cursor/skills/prototype/SKILL.md +5 -19
- package/template/.cursor/skills/prototype-review/SKILL.md +11 -0
- package/template/.cursor/skills/prototype-review/references/component-qa.md +32 -0
- package/template/.cursor/skills/setup-matt-pocock-skills/SKILL.md +4 -74
- package/template/.cursor/skills/setup-matt-pocock-skills/references/configuration-decisions.md +42 -0
- package/template/.cursor/skills/setup-matt-pocock-skills/references/write-configuration.md +42 -0
- package/template/.cursor/skills/to-spec/SKILL.md +9 -62
- package/template/.cursor/skills/to-tickets/SKILL.md +20 -86
- package/template/.cursor/skills/using-git-worktrees/SKILL.md +5 -122
- package/template/.cursor/skills/using-git-worktrees/references/git-fallback.md +46 -0
- package/template/.cursor/skills/using-git-worktrees/references/setup-and-baseline.md +45 -0
- package/template/.cursor/skills/using-git-worktrees/references/troubleshooting.md +47 -0
- package/template/.cursor/skills/wayfinder/SKILL.md +7 -86
- package/template/.cursor/skills/wayfinder/references/chart-map.md +14 -0
- package/template/.cursor/skills/wayfinder/references/map-format.md +49 -0
- package/template/.cursor/skills/wayfinder/references/scope-and-frontier.md +24 -0
- package/template/.cursor/skills/wayfinder/references/ticket-types.md +12 -0
- package/template/.cursor/skills/wayfinder/references/work-map.md +15 -0
- package/template/.cursor/skills/writing-for-agents/SKILL-MECHANICS.md +20 -11
- package/template/.cursor/skills/yedit-table-usage/SKILL.md +5 -172
- package/template/.cursor/skills/yedit-table-usage/references/editable-table-example.md +141 -0
- package/template/.cursor/skills/yedit-table-usage/references/select-and-filter-examples.md +38 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +4 -25
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/engineering-checks.md +11 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/generation-workflow.md +14 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/platform-selection.md +16 -0
- package/template/.cursor/skills/yss-design-system/SKILL.md +13 -3
- package/template/.cursor/skills/yss-design-system/assets/enterprise-craft/app.js +53 -0
- package/template/.cursor/skills/yss-design-system/assets/enterprise-craft/index.html +2 -0
- package/template/.cursor/skills/yss-design-system/assets/enterprise-craft/scenarios.js +14 -0
- package/template/.cursor/skills/yss-design-system/assets/enterprise-craft/styles.css +32 -0
- package/template/.cursor/skills/yss-design-system/references/data-quality-theme.md +2 -2
- package/template/.cursor/skills/yss-design-system/references/design-qa-theme.md +1 -1
- package/template/.cursor/skills/yss-design-system/references/design-system.md +3 -3
- package/template/.cursor/skills/yss-design-system/references/enterprise-craft.md +33 -0
- package/template/.cursor/skills/yss-design-system/references/page-composition.md +24 -0
- package/template/.cursor/skills/yss-design-system/references/prototype-default-theme.md +26 -0
- package/template/.cursor/skills/yss-design-system/references/search-date-theme.md +19 -0
- package/template/.cursor/skills/yss-design-system/scripts/export-craft-examples.mjs +30 -0
- package/template/.cursor/skills/yss-hook/SKILL.md +8 -102
- package/template/.cursor/skills/yss-hook/references/async-actions.md +49 -0
- package/template/.cursor/skills/yss-hook/references/list-pagination.md +46 -0
- package/template/.cursor/skills/yss-hook/references/polling.md +20 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +8 -3
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/scaffold-compilation.md +7 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +2 -49
- package/template/.cursor/skills/yss-openapi-draft-review/references/review-output.md +45 -0
- package/template/.cursor/skills/yss-openapi-draft-review/references/semantic-review.md +16 -0
- package/template/.cursor/skills/yss-openapi-governance/SKILL.md +2 -48
- package/template/.cursor/skills/yss-openapi-governance/references/governance-workflow.md +38 -0
- package/template/.cursor/skills/yss-openapi-governance/references/locked-toolchain.md +22 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +24 -20
- package/template/.cursor/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +429 -384
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/plan-requirements.md +12 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +3 -3
- package/template/.cursor/skills/yss-prototype-stage/SKILL.md +27 -8
- package/template/.cursor/skills/yss-prototype-stage/assets/comparison/comparison.css +12 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/comparison/comparison.js +52 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/comparison/index.html +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/comparison/review-ready.js +2 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/low-fidelity-exercise/app.js +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/low-fidelity-exercise/index.html +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/low-fidelity-exercise/styles.css +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/native-workbench/app.js +12 -6
- package/template/.cursor/skills/yss-prototype-stage/assets/native-workbench/index.html +1 -1
- package/template/.cursor/skills/yss-prototype-stage/assets/native-workbench/scenarios.json +297 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/native-workbench/styles.css +2 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/offline-html/app.js +7 -5
- package/template/.cursor/skills/yss-prototype-stage/assets/offline-html/scenarios.json +297 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/prototype-theme.css +92 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/scenario-runtime.js +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/SHADCN-LICENSE.txt +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/package.json +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/pnpm-lock.yaml +1278 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/registry-manifest.json +936 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/theme.css +107 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/Alert.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertDescription.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertTitle.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/index.ts +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialog.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogAction.vue +18 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogCancel.vue +25 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogContent.vue +44 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogDescription.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogFooter.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogHeader.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTitle.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/index.ts +9 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/Badge.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/index.ts +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/Breadcrumb.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbList.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/index.ts +7 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/Button.vue +31 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/index.ts +40 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/Card.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardAction.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardContent.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardDescription.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardFooter.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardHeader.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardTitle.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/index.ts +7 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/Checkbox.vue +35 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/Combobox.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxAnchor.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxEmpty.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxGroup.vue +27 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxInput.vue +42 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItem.vue +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItemIndicator.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxList.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxSeparator.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxTrigger.vue +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxViewport.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/index.ts +13 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/Dialog.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogClose.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogContent.vue +53 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogDescription.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogFooter.vue +27 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogHeader.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogOverlay.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogScrollContent.vue +59 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTitle.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTrigger.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/index.ts +10 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/index.ts +16 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/Empty.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyContent.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyDescription.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyHeader.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyMedia.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyTitle.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/index.ts +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/Field.vue +25 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldContent.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldDescription.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldError.vue +53 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldGroup.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLabel.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLegend.vue +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSeparator.vue +29 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSet.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldTitle.vue +20 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/index.ts +39 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/Input.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroup.vue +35 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupAddon.vue +36 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupButton.vue +29 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupInput.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupText.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupTextarea.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/index.ts +51 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/Label.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/Pagination.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationContent.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationEllipsis.vue +25 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationFirst.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationItem.vue +34 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationLast.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationNext.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationPrevious.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/index.ts +8 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/Popover.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverAnchor.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverContent.vue +45 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverTrigger.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/index.ts +4 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendar.vue +62 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCell.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCellTrigger.vue +41 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGrid.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridBody.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridHead.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridRow.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeadCell.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeader.vue +23 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeading.vue +30 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarNextButton.vue +32 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarPrevButton.vue +32 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/index.ts +12 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/Select.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectContent.vue +51 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectGroup.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItem.vue +44 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItemText.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectLabel.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollDownButton.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollUpButton.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectSeparator.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectTrigger.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectValue.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/index.ts +11 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/Separator.vue +29 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/Sheet.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetClose.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetContent.vue +62 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetDescription.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetFooter.vue +16 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetHeader.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetOverlay.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTitle.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTrigger.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/index.ts +8 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/Skeleton.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/Spinner.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/Table.vue +16 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableBody.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCaption.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCell.vue +22 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableEmpty.vue +34 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableFooter.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHead.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHeader.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableRow.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/index.ts +9 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/Tabs.vue +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsContent.vue +21 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsList.vue +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsTrigger.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/index.ts +4 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/Textarea.vue +28 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/index.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/Tooltip.vue +19 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipContent.vue +34 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipProvider.vue +14 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipTrigger.vue +15 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/index.ts +4 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/shadcn-vue-authoring/utils.ts +7 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/Analysis.vue +16 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/Approval.vue +14 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/CombinedQuery.vue +17 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/ComponentStates.vue +41 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/Conflict.vue +33 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/DateRange.vue +29 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/Frame.vue +7 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/ListDetail.vue +47 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/MultiStep.vue +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/Notice.vue +5 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/OwnerValidity.vue +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/SearchSelect.vue +32 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/Workspace.vue +174 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/analysis.scenarios.json +276 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/approval.scenarios.json +207 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.css +14 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.js +24 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query.scenarios.json +298 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/conflict.scenarios.json +229 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail.scenarios.json +185 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/mount.ts +4 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step.scenarios.json +188 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity.scenarios.json +298 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/patterns.css +26 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/refinement.css +129 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workbench.ts +1 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-definition.ts +40 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-model.js +75 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-page.ts +18 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.config.json +6 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.ts +3 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.css +101 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.scenarios.json +2015 -0
- package/template/.cursor/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.ts +26 -0
- package/template/.cursor/skills/yss-prototype-stage/references/business-patterns.md +42 -0
- package/template/.cursor/skills/yss-prototype-stage/references/comparison-tool.md +48 -0
- package/template/.cursor/skills/yss-prototype-stage/references/component-recipes.md +41 -0
- package/template/.cursor/skills/yss-prototype-stage/references/concept-comparison.md +26 -0
- package/template/.cursor/skills/yss-prototype-stage/references/enterprise-workspace.md +53 -0
- package/template/.cursor/skills/yss-prototype-stage/references/low-fidelity-exercise.md +32 -0
- package/template/.cursor/skills/yss-prototype-stage/references/product-design-adapter.md +11 -5
- package/template/.cursor/skills/yss-prototype-stage/references/prototype-profile-routing.md +10 -6
- package/template/.cursor/skills/yss-prototype-stage/references/prototype-verification.md +25 -0
- package/template/.cursor/skills/yss-prototype-stage/references/resource-lifecycle.md +20 -0
- package/template/.cursor/skills/yss-prototype-stage/references/search-date-patterns.md +28 -0
- package/template/.cursor/skills/yss-prototype-stage/references/shadcn-integration.md +5 -0
- package/template/.cursor/skills/yss-prototype-stage/references/shadcn-vue-integration.md +46 -0
- package/template/.cursor/skills/yss-prototype-stage/references/usability-research.md +26 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/browser-session.mjs +57 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/build-shadcn-vue-prototype.mjs +137 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/compose-workspace-scenarios.mjs +22 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/export-business-patterns.mjs +7 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/export-low-fidelity-exercise.mjs +18 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/export-vue-patterns.mjs +21 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/offline-html.mjs +60 -23
- package/template/.cursor/skills/yss-prototype-stage/scripts/prototype-comparison.mjs +208 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/prototype-contract.mjs +19 -14
- package/template/.cursor/skills/yss-prototype-stage/scripts/scenario-contract.mjs +31 -0
- package/template/.cursor/skills/yss-prototype-stage/scripts/verify-prototype-design.mjs +97 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/antd-reference.test.mjs +4 -24
- package/template/.cursor/skills/yss-prototype-stage/tests/browser-harness.mjs +37 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/browser-visual.mjs +22 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/business-controls-browser.mjs +43 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/business-controls.test.mjs +20 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/compact-theme-browser.mjs +53 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/comparison-browser-smoke.mjs +77 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/comparison-fixture.mjs +16 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/comparison.test.mjs +46 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/components-browser.mjs +60 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/patterns-browser.mjs +84 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/reliability.test.mjs +34 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/retirement.test.mjs +8 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/run-scenarios.mjs +19 -2
- package/template/.cursor/skills/yss-prototype-stage/tests/shadcn-vue.test.mjs +43 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/verification-entry.test.mjs +16 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/workbench-smoke.mjs +36 -21
- package/template/.cursor/skills/yss-prototype-stage/tests/workspace-browser.mjs +50 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/workspace.test.mjs +70 -0
- package/template/.cursor/skills/yss-prototype-stage/tests/zoom-browser.mjs +26 -0
- package/template/.cursor/skills/yss-research/SKILL.md +4 -0
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.cursor/skills/yss-technical-design/SKILL.md +4 -0
- package/template/.cursor/skills/yss-ui-business-page-generation/SKILL.md +4 -61
- package/template/.cursor/skills/yss-ui-business-page-generation/references/page-composition-example.md +50 -0
- package/template/.cursor/skills/yss-ui-business-page-generation/references/page-layout-example.md +19 -0
- package/template/.cursor/skills/ytable-usage/SKILL.md +3 -191
- package/template/.cursor/skills/ytable-usage/references/selection-and-batch.md +30 -0
- package/template/.cursor/skills/ytable-usage/references/table-pagination-examples.md +170 -0
- package/template/.pi/skills/archify/SKILL.md +4 -52
- package/template/.pi/skills/archify/references/authoring-notes.md +12 -0
- package/template/.pi/skills/archify/references/authoring-workflow.md +25 -0
- package/template/.pi/skills/archify/references/delivery-workflow.md +25 -0
- package/template/.pi/skills/archify/references/upstream-updates.md +12 -0
- package/template/.pi/skills/code-review/SKILL.md +3 -34
- package/template/.pi/skills/code-review/references/legacy-convergence.md +5 -0
- package/template/.pi/skills/code-review/references/review-axis-checklists.md +18 -0
- package/template/.pi/skills/code-review/references/smell-baseline.md +23 -0
- package/template/.pi/skills/codebase-design/SKILL.md +4 -0
- package/template/.pi/skills/codebase-design/references/architecture-audit.md +69 -0
- package/template/.pi/skills/diagnosing-bugs/SKILL.md +3 -91
- package/template/.pi/skills/diagnosing-bugs/references/feedback-loop.md +53 -0
- package/template/.pi/skills/diagnosing-bugs/references/investigation.md +49 -0
- package/template/.pi/skills/frontend-commit/SKILL.md +4 -93
- package/template/.pi/skills/git-commit-core/SKILL.md +18 -0
- package/template/.pi/skills/git-commit-core/agents/openai.yaml +2 -0
- package/template/.pi/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.pi/skills/llm-wiki/SKILL.md +12 -13
- package/template/.pi/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.pi/skills/llm-wiki/references/compile.md +21 -114
- package/template/.pi/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.pi/skills/llm-wiki/references/lint.md +9 -46
- package/template/.pi/skills/llm-wiki/references/query.md +9 -18
- package/template/.pi/skills/llm-wiki/references/schema.md +56 -29
- package/template/.pi/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.pi/skills/llm-wiki/references/writing.md +4 -4
- package/template/.pi/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.pi/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.pi/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.pi/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.pi/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.pi/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.pi/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.pi/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.pi/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.pi/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.pi/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.pi/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.pi/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.pi/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.pi/skills/maintaining-skills/references/authoring.md +6 -0
- package/template/.pi/skills/prototype/LOGIC.md +1 -1
- package/template/.pi/skills/prototype/SKILL.md +5 -19
- package/template/.pi/skills/prototype-review/SKILL.md +11 -0
- package/template/.pi/skills/prototype-review/references/component-qa.md +32 -0
- package/template/.pi/skills/setup-matt-pocock-skills/SKILL.md +4 -74
- package/template/.pi/skills/setup-matt-pocock-skills/references/configuration-decisions.md +42 -0
- package/template/.pi/skills/setup-matt-pocock-skills/references/write-configuration.md +42 -0
- package/template/.pi/skills/to-spec/SKILL.md +9 -62
- package/template/.pi/skills/to-tickets/SKILL.md +20 -86
- package/template/.pi/skills/using-git-worktrees/SKILL.md +5 -122
- package/template/.pi/skills/using-git-worktrees/references/git-fallback.md +46 -0
- package/template/.pi/skills/using-git-worktrees/references/setup-and-baseline.md +45 -0
- package/template/.pi/skills/using-git-worktrees/references/troubleshooting.md +47 -0
- package/template/.pi/skills/wayfinder/SKILL.md +7 -86
- package/template/.pi/skills/wayfinder/references/chart-map.md +14 -0
- package/template/.pi/skills/wayfinder/references/map-format.md +49 -0
- package/template/.pi/skills/wayfinder/references/scope-and-frontier.md +24 -0
- package/template/.pi/skills/wayfinder/references/ticket-types.md +12 -0
- package/template/.pi/skills/wayfinder/references/work-map.md +15 -0
- package/template/.pi/skills/writing-for-agents/SKILL-MECHANICS.md +20 -11
- package/template/.pi/skills/yedit-table-usage/SKILL.md +5 -172
- package/template/.pi/skills/yedit-table-usage/references/editable-table-example.md +141 -0
- package/template/.pi/skills/yedit-table-usage/references/select-and-filter-examples.md +38 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +4 -25
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/engineering-checks.md +11 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/generation-workflow.md +14 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/platform-selection.md +16 -0
- package/template/.pi/skills/yss-design-system/SKILL.md +13 -3
- package/template/.pi/skills/yss-design-system/assets/enterprise-craft/app.js +53 -0
- package/template/.pi/skills/yss-design-system/assets/enterprise-craft/index.html +2 -0
- package/template/.pi/skills/yss-design-system/assets/enterprise-craft/scenarios.js +14 -0
- package/template/.pi/skills/yss-design-system/assets/enterprise-craft/styles.css +32 -0
- package/template/.pi/skills/yss-design-system/references/data-quality-theme.md +2 -2
- package/template/.pi/skills/yss-design-system/references/design-qa-theme.md +1 -1
- package/template/.pi/skills/yss-design-system/references/design-system.md +3 -3
- package/template/.pi/skills/yss-design-system/references/enterprise-craft.md +33 -0
- package/template/.pi/skills/yss-design-system/references/page-composition.md +24 -0
- package/template/.pi/skills/yss-design-system/references/prototype-default-theme.md +26 -0
- package/template/.pi/skills/yss-design-system/references/search-date-theme.md +19 -0
- package/template/.pi/skills/yss-design-system/scripts/export-craft-examples.mjs +30 -0
- package/template/.pi/skills/yss-hook/SKILL.md +8 -102
- package/template/.pi/skills/yss-hook/references/async-actions.md +49 -0
- package/template/.pi/skills/yss-hook/references/list-pagination.md +46 -0
- package/template/.pi/skills/yss-hook/references/polling.md +20 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +8 -3
- package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.pi/skills/yss-implementation-contract-compiler/references/scaffold-compilation.md +7 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +2 -49
- package/template/.pi/skills/yss-openapi-draft-review/references/review-output.md +45 -0
- package/template/.pi/skills/yss-openapi-draft-review/references/semantic-review.md +16 -0
- package/template/.pi/skills/yss-openapi-governance/SKILL.md +2 -48
- package/template/.pi/skills/yss-openapi-governance/references/governance-workflow.md +38 -0
- package/template/.pi/skills/yss-openapi-governance/references/locked-toolchain.md +22 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +24 -20
- package/template/.pi/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +429 -384
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.pi/skills/yss-product-lifecycle/references/plan-requirements.md +12 -0
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +3 -3
- package/template/.pi/skills/yss-prototype-stage/SKILL.md +27 -8
- package/template/.pi/skills/yss-prototype-stage/assets/comparison/comparison.css +12 -0
- package/template/.pi/skills/yss-prototype-stage/assets/comparison/comparison.js +52 -0
- package/template/.pi/skills/yss-prototype-stage/assets/comparison/index.html +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/comparison/review-ready.js +2 -0
- package/template/.pi/skills/yss-prototype-stage/assets/low-fidelity-exercise/app.js +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/low-fidelity-exercise/index.html +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/low-fidelity-exercise/styles.css +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/native-workbench/app.js +12 -6
- package/template/.pi/skills/yss-prototype-stage/assets/native-workbench/index.html +1 -1
- package/template/.pi/skills/yss-prototype-stage/assets/native-workbench/scenarios.json +297 -0
- package/template/.pi/skills/yss-prototype-stage/assets/native-workbench/styles.css +2 -0
- package/template/.pi/skills/yss-prototype-stage/assets/offline-html/app.js +7 -5
- package/template/.pi/skills/yss-prototype-stage/assets/offline-html/scenarios.json +297 -0
- package/template/.pi/skills/yss-prototype-stage/assets/prototype-theme.css +92 -0
- package/template/.pi/skills/yss-prototype-stage/assets/scenario-runtime.js +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/SHADCN-LICENSE.txt +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/package.json +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/pnpm-lock.yaml +1278 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/registry-manifest.json +936 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/theme.css +107 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/Alert.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertDescription.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/AlertTitle.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert/index.ts +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialog.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogAction.vue +18 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogCancel.vue +25 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogContent.vue +44 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogDescription.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogFooter.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogHeader.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTitle.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/alert-dialog/index.ts +9 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/Badge.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/badge/index.ts +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/Breadcrumb.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbList.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/breadcrumb/index.ts +7 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/Button.vue +31 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/button/index.ts +40 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/Card.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardAction.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardContent.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardDescription.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardFooter.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardHeader.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/CardTitle.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/card/index.ts +7 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/Checkbox.vue +35 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/checkbox/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/Combobox.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxAnchor.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxEmpty.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxGroup.vue +27 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxInput.vue +42 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItem.vue +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxItemIndicator.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxList.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxSeparator.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxTrigger.vue +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/ComboboxViewport.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/combobox/index.ts +13 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/Dialog.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogClose.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogContent.vue +53 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogDescription.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogFooter.vue +27 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogHeader.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogOverlay.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogScrollContent.vue +59 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTitle.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/DialogTrigger.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dialog/index.ts +10 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +39 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuGroup.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuItem.vue +31 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/dropdown-menu/index.ts +16 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/Empty.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyContent.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyDescription.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyHeader.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyMedia.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/EmptyTitle.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/empty/index.ts +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/Field.vue +25 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldContent.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldDescription.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldError.vue +53 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldGroup.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLabel.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldLegend.vue +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSeparator.vue +29 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldSet.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/FieldTitle.vue +20 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/field/index.ts +39 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/Input.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroup.vue +35 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupAddon.vue +36 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupButton.vue +29 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupInput.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupText.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/InputGroupTextarea.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/input-group/index.ts +51 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/Label.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/label/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/Pagination.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationContent.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationEllipsis.vue +25 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationFirst.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationItem.vue +34 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationLast.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationNext.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/PaginationPrevious.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/pagination/index.ts +8 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/Popover.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverAnchor.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverContent.vue +45 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/PopoverTrigger.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/popover/index.ts +4 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendar.vue +62 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCell.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarCellTrigger.vue +41 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGrid.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridBody.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridHead.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarGridRow.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeadCell.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeader.vue +23 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarHeading.vue +30 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarNextButton.vue +32 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/RangeCalendarPrevButton.vue +32 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/range-calendar/index.ts +12 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/Select.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectContent.vue +51 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectGroup.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItem.vue +44 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectItemText.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectLabel.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollDownButton.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectScrollUpButton.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectSeparator.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectTrigger.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/SelectValue.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/select/index.ts +11 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/Separator.vue +29 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/separator/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/Sheet.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetClose.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetContent.vue +62 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetDescription.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetFooter.vue +16 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetHeader.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetOverlay.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTitle.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/SheetTrigger.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/sheet/index.ts +8 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/Skeleton.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/skeleton/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/Spinner.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/spinner/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/Table.vue +16 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableBody.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCaption.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableCell.vue +22 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableEmpty.vue +34 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableFooter.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHead.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableHeader.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/TableRow.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/table/index.ts +9 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/Tabs.vue +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsContent.vue +21 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsList.vue +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/TabsTrigger.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tabs/index.ts +4 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/Textarea.vue +28 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/textarea/index.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/Tooltip.vue +19 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipContent.vue +34 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipProvider.vue +14 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/TooltipTrigger.vue +15 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/ui/tooltip/index.ts +4 -0
- package/template/.pi/skills/yss-prototype-stage/assets/shadcn-vue-authoring/utils.ts +7 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/Analysis.vue +16 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/Approval.vue +14 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/CombinedQuery.vue +17 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/ComponentStates.vue +41 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/Conflict.vue +33 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/DateRange.vue +29 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/Frame.vue +7 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/ListDetail.vue +47 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/MultiStep.vue +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/Notice.vue +5 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/OwnerValidity.vue +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/SearchSelect.vue +32 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/Workspace.vue +174 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/analysis-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/analysis.scenarios.json +276 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/approval-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/approval.scenarios.json +207 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.css +14 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/business-controls.js +24 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/combined-query.scenarios.json +298 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/component-states-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/conflict-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/conflict.scenarios.json +229 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-glass.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/list-detail.scenarios.json +185 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/mount.ts +4 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-glass.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/multi-step.scenarios.json +188 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/owner-validity.scenarios.json +298 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/patterns.css +26 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/refinement.css +129 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workbench.ts +1 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-definition.ts +40 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-glass.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-model.js +75 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-page.ts +18 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.config.json +6 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace-standard.ts +3 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.css +101 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.scenarios.json +2015 -0
- package/template/.pi/skills/yss-prototype-stage/assets/vue-business-patterns/workspace.ts +26 -0
- package/template/.pi/skills/yss-prototype-stage/references/business-patterns.md +42 -0
- package/template/.pi/skills/yss-prototype-stage/references/comparison-tool.md +48 -0
- package/template/.pi/skills/yss-prototype-stage/references/component-recipes.md +41 -0
- package/template/.pi/skills/yss-prototype-stage/references/concept-comparison.md +26 -0
- package/template/.pi/skills/yss-prototype-stage/references/enterprise-workspace.md +53 -0
- package/template/.pi/skills/yss-prototype-stage/references/low-fidelity-exercise.md +32 -0
- package/template/.pi/skills/yss-prototype-stage/references/product-design-adapter.md +11 -5
- package/template/.pi/skills/yss-prototype-stage/references/prototype-profile-routing.md +10 -6
- package/template/.pi/skills/yss-prototype-stage/references/prototype-verification.md +25 -0
- package/template/.pi/skills/yss-prototype-stage/references/resource-lifecycle.md +20 -0
- package/template/.pi/skills/yss-prototype-stage/references/search-date-patterns.md +28 -0
- package/template/.pi/skills/yss-prototype-stage/references/shadcn-integration.md +5 -0
- package/template/.pi/skills/yss-prototype-stage/references/shadcn-vue-integration.md +46 -0
- package/template/.pi/skills/yss-prototype-stage/references/usability-research.md +26 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/browser-session.mjs +57 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/build-shadcn-vue-prototype.mjs +137 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/compose-workspace-scenarios.mjs +22 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/export-business-patterns.mjs +7 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/export-low-fidelity-exercise.mjs +18 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/export-vue-patterns.mjs +21 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/offline-html.mjs +60 -23
- package/template/.pi/skills/yss-prototype-stage/scripts/prototype-comparison.mjs +208 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/prototype-contract.mjs +19 -14
- package/template/.pi/skills/yss-prototype-stage/scripts/scenario-contract.mjs +31 -0
- package/template/.pi/skills/yss-prototype-stage/scripts/verify-prototype-design.mjs +97 -0
- package/template/.pi/skills/yss-prototype-stage/tests/antd-reference.test.mjs +4 -24
- package/template/.pi/skills/yss-prototype-stage/tests/browser-harness.mjs +37 -0
- package/template/.pi/skills/yss-prototype-stage/tests/browser-visual.mjs +22 -0
- package/template/.pi/skills/yss-prototype-stage/tests/business-controls-browser.mjs +43 -0
- package/template/.pi/skills/yss-prototype-stage/tests/business-controls.test.mjs +20 -0
- package/template/.pi/skills/yss-prototype-stage/tests/compact-theme-browser.mjs +53 -0
- package/template/.pi/skills/yss-prototype-stage/tests/comparison-browser-smoke.mjs +77 -0
- package/template/.pi/skills/yss-prototype-stage/tests/comparison-fixture.mjs +16 -0
- package/template/.pi/skills/yss-prototype-stage/tests/comparison.test.mjs +46 -0
- package/template/.pi/skills/yss-prototype-stage/tests/components-browser.mjs +60 -0
- package/template/.pi/skills/yss-prototype-stage/tests/patterns-browser.mjs +84 -0
- package/template/.pi/skills/yss-prototype-stage/tests/reliability.test.mjs +34 -0
- package/template/.pi/skills/yss-prototype-stage/tests/retirement.test.mjs +8 -0
- package/template/.pi/skills/yss-prototype-stage/tests/run-scenarios.mjs +19 -2
- package/template/.pi/skills/yss-prototype-stage/tests/shadcn-vue.test.mjs +43 -0
- package/template/.pi/skills/yss-prototype-stage/tests/verification-entry.test.mjs +16 -0
- package/template/.pi/skills/yss-prototype-stage/tests/workbench-smoke.mjs +36 -21
- package/template/.pi/skills/yss-prototype-stage/tests/workspace-browser.mjs +50 -0
- package/template/.pi/skills/yss-prototype-stage/tests/workspace.test.mjs +70 -0
- package/template/.pi/skills/yss-prototype-stage/tests/zoom-browser.mjs +26 -0
- package/template/.pi/skills/yss-research/SKILL.md +4 -0
- package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -0
- package/template/.pi/skills/yss-technical-design/SKILL.md +4 -0
- package/template/.pi/skills/yss-ui-business-page-generation/SKILL.md +4 -61
- package/template/.pi/skills/yss-ui-business-page-generation/references/page-composition-example.md +50 -0
- package/template/.pi/skills/yss-ui-business-page-generation/references/page-layout-example.md +19 -0
- package/template/.pi/skills/ytable-usage/SKILL.md +3 -191
- package/template/.pi/skills/ytable-usage/references/selection-and-batch.md +30 -0
- package/template/.pi/skills/ytable-usage/references/table-pagination-examples.md +170 -0
- package/template/.template-spec/agents/README.md +1 -1
- package/template/.template-spec/agents/digital-human-roles.yaml +3 -3
- package/template/.template-spec/agents/issue-tracker.md +10 -1
- package/template/.template-spec/agents/skill-migrations.md +15 -0
- package/template/.template-spec/agents/yss-skill-registry.yaml +51 -29
- package/template/.template-spec/architecture/README.md +3 -34
- package/template/.template-spec/design/README.md +7 -2
- package/template/.template-spec/design/design.md +30 -35
- package/template/.template-spec/design/templates/examples/model-publishing-interaction.example.md +2 -2
- package/template/.template-spec/design/templates/interaction-spec-template.md +24 -0
- package/template/.template-spec/design/templates/prototype-evidence-template.yaml +18 -5
- package/template/.template-spec/design/templates/prototype-review-checklist.md +8 -0
- package/template/.template-spec/design/tokens/.design-md-projection.json +2 -2
- package/template/.template-spec/design/tokens/variables.css +162 -0
- package/template/.template-spec/plan/templates/competitive-analysis-template.md +2 -2
- package/template/.template-spec/plan/templates/plan-template.md +25 -1
- package/template/.template-spec/process/business-tickets.md +48 -0
- package/template/.template-spec/process/checkpoint-boundary.yaml +73 -0
- package/template/.template-spec/process/contract-reading.md +59 -0
- package/template/.template-spec/process/document-writing.md +2 -0
- package/template/.template-spec/process/feature-assets.md +38 -0
- package/template/.template-spec/process/lifecycle-artifact-map.md +1 -0
- package/template/.template-spec/process/lifecycle-registry-baseline.json +10 -4
- package/template/.template-spec/process/lifecycle-registry.yaml +21 -5
- package/template/.template-spec/process/plan-spec-quality.md +69 -0
- package/template/.template-spec/process/project-ci.md +21 -0
- package/template/.template-spec/process/reading-policy.yaml +4 -0
- package/template/.template-spec/process/research-completion.md +23 -0
- package/template/.template-spec/process/schemas/business-tickets-v1.schema.json +449 -0
- package/template/.template-spec/process/schemas/digital-human-task-package.schema.json +202 -12
- package/template/.template-spec/process/schemas/domain-strategy-reading.schema.json +216 -0
- package/template/.template-spec/process/schemas/frontend-implementation-evidence.schema.json +7 -15
- package/template/.template-spec/process/schemas/lifecycle-checkpoint.schema.json +203 -11
- package/template/.template-spec/process/schemas/reading-manifest.schema.json +14 -0
- package/template/.template-spec/process/schemas/reading-policy.schema.json +10 -0
- package/template/.template-spec/process/schemas/stage-decision-package-reading.schema.json +148 -0
- package/template/.template-spec/process/stage-tracking.md +8 -0
- package/template/.template-spec/process/strategic-handoff-package.md +3 -1
- package/template/.template-spec/process/subagent-collaboration.md +8 -0
- package/template/.template-spec/process/templates/frontend-implementation-verification-template.yaml +15 -1
- package/template/.template-spec/process/templates/stage-checkpoint-template.md +6 -1
- package/template/.template-spec/templates/business-ticket-review-template.yaml +11 -0
- package/template/.template-spec/templates/business-ticket-set-template.yaml +16 -0
- package/template/.template-spec/templates/business-ticket-template.md +38 -0
- package/template/.template-spec/templates/examples/lifecycle-writing-examples.md +16 -0
- package/template/.template-spec/templates/examples/plan-spec/complex-defective.md +38 -0
- package/template/.template-spec/templates/examples/plan-spec/complex-source.md +12 -0
- package/template/.template-spec/templates/examples/plan-spec/complex-valid.md +38 -0
- package/template/.template-spec/templates/examples/plan-spec/expectations.json +46 -0
- package/template/.template-spec/templates/examples/plan-spec/high-risk-before.md +32 -0
- package/template/.template-spec/templates/examples/plan-spec/high-risk-defective.md +39 -0
- package/template/.template-spec/templates/examples/plan-spec/high-risk-source.md +11 -0
- package/template/.template-spec/templates/examples/plan-spec/high-risk-valid.md +39 -0
- package/template/.template-spec/templates/examples/plan-spec/ordinary-defective.md +36 -0
- package/template/.template-spec/templates/examples/plan-spec/ordinary-plan.md +27 -0
- package/template/.template-spec/templates/examples/plan-spec/ordinary-source.md +14 -0
- package/template/.template-spec/templates/examples/plan-spec/ordinary-valid.md +36 -0
- package/template/.template-spec/templates/local-parent-ticket-template.md +23 -15
- package/template/.template-spec/templates/spec-delta-template.md +7 -5
- package/template/.template-spec/templates/spec-template.md +31 -15
- package/template/.template-spec/templates/vertical-slice-ticket-template.md +4 -0
- package/template/AGENTS.md +2 -2
- package/template/CONTEXT.md +6 -1
- package/template/DESIGN.md +23 -4
- package/template/README.md +12 -24
- package/template/scripts/contract +17 -5
- package/template/scripts/feature-assets +23 -0
- package/template/scripts/inspect-plan-spec +32 -0
- package/template/scripts/inspect-skill-discovery +24 -0
- package/template/scripts/lib/business-ticket-lifecycle.mjs +34 -0
- package/template/scripts/lib/business-tickets.mjs +273 -0
- package/template/scripts/lib/checkpoint-boundary.mjs +40 -0
- package/template/scripts/lib/contract-views.mjs +77 -9
- package/template/scripts/lib/execution-evidence.mjs +51 -0
- package/template/scripts/lib/feature-assets.mjs +274 -0
- package/template/scripts/lib/governance-io.mjs +40 -0
- package/template/scripts/lib/harness-execution-scope.mjs +1 -1
- package/template/scripts/lib/implementation-contract-compiler.mjs +2 -0
- package/template/scripts/lib/lifecycle-context-query.mjs +15 -3
- package/template/scripts/lib/lifecycle-status.mjs +49 -7
- package/template/scripts/lib/lifecycle-transition.mjs +31 -4
- package/template/scripts/lib/maintenance-research.mjs +63 -0
- package/template/scripts/lib/plan-spec-markdown.mjs +97 -0
- package/template/scripts/lib/plan-spec-quality.mjs +289 -0
- package/template/scripts/lib/project-ci-install.mjs +98 -0
- package/template/scripts/lib/project-governance.mjs +189 -0
- package/template/scripts/lib/read-only-intake.mjs +132 -0
- package/template/scripts/lib/reading-view-adapters.mjs +62 -0
- package/template/scripts/lib/reading-view-bundle.mjs +201 -0
- package/template/scripts/lib/reading-view-diff.mjs +29 -0
- package/template/scripts/lib/reading-view-markdown.mjs +39 -0
- package/template/scripts/lib/reading-view-model.mjs +13 -0
- package/template/scripts/lib/reading-view-policy.mjs +16 -0
- package/template/scripts/lib/skill-discovery.mjs +150 -0
- package/template/scripts/lib/skill-registry.mjs +43 -2
- package/template/scripts/lib/skill-supply-chain.mjs +5 -1
- package/template/scripts/lib/slice-contract-preparation.mjs +8 -2
- package/template/scripts/lib/slice-contract.mjs +15 -1
- package/template/scripts/lib/slice-task-package.mjs +1 -1
- package/template/scripts/lib/stage-tracking-migration.mjs +9 -1
- package/template/scripts/lib/stage-tracking.mjs +10 -5
- package/template/scripts/lib/strategic-handoff-consumption.mjs +14 -2
- package/template/scripts/lib/strategic-handoff-io.mjs +6 -2
- package/template/scripts/lib/strategic-handoff.mjs +26 -5
- package/template/scripts/lib/task-package.mjs +22 -4
- package/template/scripts/lib/testing/user-decision-fixture.mjs +33 -0
- package/template/scripts/lib/user-decision-reuse.mjs +1 -1
- package/template/scripts/lifecycle-status +13 -3
- package/template/scripts/prepare-read-only-intake +16 -0
- package/template/scripts/project-ci +20 -0
- package/template/scripts/project-ci-transition +13 -0
- package/template/scripts/query-lifecycle-context +4 -0
- package/template/scripts/run-read-only-intake +14 -0
- package/template/scripts/stage-tracking +1 -0
- package/template/scripts/vendor/NOTICES.md +237 -0
- package/template/scripts/vendor/manifest.json +1216 -65
- package/template/scripts/vendor/markdown.mjs +11783 -0
- package/template/scripts/verify-approval-record +4 -1
- package/template/scripts/verify-business-tickets +11 -0
- package/template/scripts/verify-context-contract +4 -1
- package/template/scripts/verify-digital-human-task-package +5 -2
- package/template/scripts/verify-frontend-implementation-evidence +24 -1
- package/template/scripts/verify-lifecycle-checkpoint +17 -2
- package/template/scripts/verify-maintenance-research +13 -0
- package/template/scripts/verify-prototype-design +3 -0
- package/template/skills-lock.json +44 -101
- package/template.manifest.json +119 -29
- package/template.snapshot.json +5 -6
- package/template/.agents/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.agents/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.agents/skills/improve-codebase-architecture/SKILL.md +0 -75
- package/template/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.agents/skills/prototype/UI.md +0 -112
- package/template/.agents/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.agents/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.agents/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.agents/skills/wait-what/SKILL.md +0 -7
- package/template/.agents/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.agents/skills/yss-prototype-stage/assets/antd-authoring/package.json +0 -16
- package/template/.agents/skills/yss-prototype-stage/assets/antd-authoring/pnpm-lock.yaml +0 -1678
- package/template/.agents/skills/yss-prototype-stage/assets/antd-authoring/workbench.jsx +0 -23
- package/template/.agents/skills/yss-prototype-stage/references/antd-component-catalog.json +0 -736
- package/template/.agents/skills/yss-prototype-stage/references/antd-integration.md +0 -61
- package/template/.agents/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +0 -71
- package/template/.agents/skills/yss-prototype-stage/scripts/collect-antd-reference.mjs +0 -26
- package/template/.codex/skills/data-analytics/.app.json +0 -84
- package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +0 -66
- package/template/.codex/skills/data-analytics/.mcp.json +0 -30
- package/template/.codex/skills/data-analytics/AGENTS.md +0 -20
- package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +0 -27
- package/template/.codex/skills/data-analytics/README.md +0 -60
- package/template/.codex/skills/data-analytics/__yss_dotfile__.gitignore +0 -2
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-small.svg +0 -5
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
- package/template/.codex/skills/data-analytics/assets/datascience.svg +0 -10
- package/template/.codex/skills/data-analytics/mcp/server.cjs +0 -2964
- package/template/.codex/skills/data-analytics/package-lock.json +0 -3048
- package/template/.codex/skills/data-analytics/package.json +0 -43
- package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +0 -75
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +0 -137
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/references/quality-checks.md +0 -29
- package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +0 -148
- package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +0 -18
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +0 -24
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +0 -71
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +0 -85
- package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +0 -207
- package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +0 -70
- package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +0 -66
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +0 -108
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +0 -1
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +0 -52
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +0 -61
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +0 -362
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +0 -829
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +0 -30
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +0 -127
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +0 -374
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +0 -613
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +0 -54
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +0 -16
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +0 -9
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/tests/test_delivery_plan.py +0 -45
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +0 -77
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +0 -2379
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +0 -88
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +0 -97
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +0 -59
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +0 -75
- package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +0 -103
- package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +0 -68
- package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/index/SKILL.md +0 -251
- package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +0 -131
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +0 -141
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +0 -32
- package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +0 -106
- package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +0 -130
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +0 -141
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +0 -178
- package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +0 -9
- package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +0 -31
- package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +0 -466
- package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +0 -99
- package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +0 -197
- package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +0 -26
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +0 -51
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +0 -66
- package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +0 -69
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +0 -204
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +0 -87
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +0 -497
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +0 -74
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +0 -65
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +0 -160
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +0 -75
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +0 -96
- package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +0 -263
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +0 -1462
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +0 -128
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +0 -101
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +0 -499
- package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +0 -978
- package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +0 -126
- package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/validate-data/references/validation-methods.md +0 -70
- package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +0 -157
- package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +0 -774
- package/template/.codex/skills/data-analytics/src/DESIGN.md +0 -222
- package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +0 -3666
- package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +0 -136
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +0 -54
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +0 -96
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +0 -1647
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +0 -245
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +0 -462
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +0 -107
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +0 -164
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +0 -192
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +0 -203
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +0 -619
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +0 -402
- package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +0 -370
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +0 -536
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +0 -681
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +0 -164
- package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +0 -21
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +0 -472
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +0 -75
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +0 -142
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +0 -142
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +0 -377
- package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +0 -3468
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +0 -44
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +0 -639
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +0 -327
- package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +0 -225
- package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +0 -204
- package/template/.codex/skills/data-analytics/src/analytics-app-core.md +0 -85
- package/template/.codex/skills/data-analytics/src/codex-style-contract.md +0 -30
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +0 -12
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +0 -1008
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +0 -2117
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +0 -122
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +0 -3588
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +0 -483
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +0 -32
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +0 -333
- package/template/.codex/skills/data-analytics/src/mcp-host.js +0 -153
- package/template/.codex/skills/data-analytics/src/recharts-config.js +0 -315
- package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +0 -294
- package/template/.codex/skills/data-analytics/src/sql-source-view.css +0 -33
- package/template/.codex/skills/data-analytics/src/sql-source-view.js +0 -157
- package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +0 -121
- package/template/.codex/skills/data-analytics/src/table-renderer.jsx +0 -28
- package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +0 -87
- package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +0 -105
- package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +0 -233
- package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +0 -1500
- package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +0 -219
- package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +0 -144
- package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +0 -11
- package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +0 -54
- package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +0 -321
- package/template/.codex/skills/data-analytics/tsconfig.json +0 -19
- package/template/.codex/skills/data-analytics/vite.config.ts +0 -46
- package/template/.codex/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.codex/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.codex/skills/improve-codebase-architecture/SKILL.md +0 -75
- package/template/.codex/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.codex/skills/prototype/UI.md +0 -112
- package/template/.codex/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.codex/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.codex/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.codex/skills/wait-what/SKILL.md +0 -7
- package/template/.codex/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.codex/skills/yss-prototype-stage/assets/antd-authoring/package.json +0 -16
- package/template/.codex/skills/yss-prototype-stage/assets/antd-authoring/pnpm-lock.yaml +0 -1678
- package/template/.codex/skills/yss-prototype-stage/assets/antd-authoring/workbench.jsx +0 -23
- package/template/.codex/skills/yss-prototype-stage/references/antd-component-catalog.json +0 -736
- package/template/.codex/skills/yss-prototype-stage/references/antd-integration.md +0 -61
- package/template/.codex/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +0 -71
- package/template/.codex/skills/yss-prototype-stage/scripts/collect-antd-reference.mjs +0 -26
- package/template/.cursor/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.cursor/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.cursor/skills/improve-codebase-architecture/SKILL.md +0 -75
- package/template/.cursor/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.cursor/skills/prototype/UI.md +0 -112
- package/template/.cursor/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.cursor/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.cursor/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.cursor/skills/wait-what/SKILL.md +0 -7
- package/template/.cursor/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.cursor/skills/yss-prototype-stage/assets/antd-authoring/package.json +0 -16
- package/template/.cursor/skills/yss-prototype-stage/assets/antd-authoring/pnpm-lock.yaml +0 -1678
- package/template/.cursor/skills/yss-prototype-stage/assets/antd-authoring/workbench.jsx +0 -23
- package/template/.cursor/skills/yss-prototype-stage/references/antd-component-catalog.json +0 -736
- package/template/.cursor/skills/yss-prototype-stage/references/antd-integration.md +0 -61
- package/template/.cursor/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +0 -71
- package/template/.cursor/skills/yss-prototype-stage/scripts/collect-antd-reference.mjs +0 -26
- package/template/.pi/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.pi/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.pi/skills/improve-codebase-architecture/SKILL.md +0 -75
- package/template/.pi/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.pi/skills/prototype/UI.md +0 -112
- package/template/.pi/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.pi/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.pi/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.pi/skills/wait-what/SKILL.md +0 -7
- package/template/.pi/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.pi/skills/yss-prototype-stage/assets/antd-authoring/package.json +0 -16
- package/template/.pi/skills/yss-prototype-stage/assets/antd-authoring/pnpm-lock.yaml +0 -1678
- package/template/.pi/skills/yss-prototype-stage/assets/antd-authoring/workbench.jsx +0 -23
- package/template/.pi/skills/yss-prototype-stage/references/antd-component-catalog.json +0 -736
- package/template/.pi/skills/yss-prototype-stage/references/antd-integration.md +0 -61
- package/template/.pi/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +0 -71
- package/template/.pi/skills/yss-prototype-stage/scripts/collect-antd-reference.mjs +0 -26
- package/template/.template-spec/agents/domain.md +0 -31
- /package/template/.agents/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.codex/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.cursor/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.pi/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
|
@@ -1,3666 +0,0 @@
|
|
|
1
|
-
import { ArrowDown, ArrowUp, Boxes, Camera, ChartArea, ChartBar, ChartColumn, ChartColumnStacked, ChartLine, ChartNoAxesColumn, ChartNoAxesCombined, ChartScatter, ChartSpline, Check, ChevronDown, ChevronLeft, ChevronRight, Copy, Database, Ellipsis, Expand, Filter as FunnelIcon, FileDown, FileText, Globe, Pencil, Presentation, RefreshCw, Table2, Tally3, Trash2, X, } from "lucide-react";
|
|
2
|
-
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { AnalyticsLayoutCanvas } from "./layout/AnalyticsLayoutCanvas";
|
|
5
|
-
import { isChartType as sharedIsChartType } from "./charting/chart-compatibility";
|
|
6
|
-
import { applyChartSpecOverride, chartEncoding, chartEncodingField, chartEncodingFields, chartEncodingLabel, chartHasEncodingSpec, chartSpecOverrideFromWidgetSpec, chartUsedFields, compatibleChartTypesFor, compatibleChartTypesForArtifactCard, withChartType } from "./charting/chart-app-helpers";
|
|
7
|
-
import { ChartRenderer } from "./charting/ChartRenderer";
|
|
8
|
-
import { RichMarkdown } from "./layout/RichMarkdown";
|
|
9
|
-
import { copyElementAsImage, copyTextToClipboard, imageCopySuccessMessage, shouldOfferImageClipboardCopy, usePreparedImageExport } from "./imageExport";
|
|
10
|
-
function resizeEditableTextarea(element) {
|
|
11
|
-
if (!element)
|
|
12
|
-
return;
|
|
13
|
-
element.style.height = "auto";
|
|
14
|
-
element.style.height = `${element.scrollHeight}px`;
|
|
15
|
-
}
|
|
16
|
-
function cloneSerializable(value) {
|
|
17
|
-
return JSON.parse(JSON.stringify(value));
|
|
18
|
-
}
|
|
19
|
-
const DEFAULT_DASHBOARD_CARD_LAYOUT = "half";
|
|
20
|
-
const DEFAULT_REPORT_CARD_LAYOUT = "full";
|
|
21
|
-
const CARD_DRAG_CANCEL_SELECTOR = ".viz-card__no-drag, button, a, input, textarea, select, [contenteditable='true'], .rich-markdown-editor, [role='button'], [role='menu'], [role='menuitem'], [role='menuitemradio']";
|
|
22
|
-
const CHART_FULLSCREEN_HEIGHT = 520;
|
|
23
|
-
const TABLE_CARD_PAGE_SIZE = 15;
|
|
24
|
-
const TABLE_COLUMN_DEFAULT_WIDTH = 144;
|
|
25
|
-
const TABLE_COLUMN_DENSE_HORIZONTAL_PADDING = 16;
|
|
26
|
-
const TABLE_COLUMN_HEADER_CHROME_WIDTH = 24;
|
|
27
|
-
const TABLE_COLUMN_KEYBOARD_STEP = 24;
|
|
28
|
-
const TABLE_COLUMN_MAX_WIDTH = 420;
|
|
29
|
-
const TABLE_COLUMN_MIN_WIDTH = 88;
|
|
30
|
-
const TABLE_COLUMN_NUMERIC_MAX_WIDTH = 136;
|
|
31
|
-
const TABLE_COLUMN_SAMPLE_SIZE = 50;
|
|
32
|
-
const TABLE_COLUMN_TEXT_MAX_WIDTH = 220;
|
|
33
|
-
const TABLE_COLUMN_LONG_TEXT_MAX_WIDTH = 260;
|
|
34
|
-
const EMPTY_ACCESS_ISSUES = [];
|
|
35
|
-
const EMPTY_CARDS = [];
|
|
36
|
-
const EMPTY_CHARTS = [];
|
|
37
|
-
const EMPTY_FILTERS = [];
|
|
38
|
-
const EMPTY_REPORT_BLOCKS = [];
|
|
39
|
-
const EMPTY_TABLES = [];
|
|
40
|
-
const KPI_COLUMN_BREAKPOINTS = [
|
|
41
|
-
{ minWidth: 760, columns: 3 },
|
|
42
|
-
{ minWidth: 520, columns: 2 },
|
|
43
|
-
{ minWidth: 392, columns: 2 }
|
|
44
|
-
];
|
|
45
|
-
function getHeatmapFill(intensity) {
|
|
46
|
-
const index = clamp(Math.floor(intensity * heatmapBlueScale.length), 0, heatmapBlueScale.length - 1);
|
|
47
|
-
return heatmapBlueScale[index];
|
|
48
|
-
}
|
|
49
|
-
async function fetchJson(path, init) {
|
|
50
|
-
const response = await fetch(path, { cache: "no-store", ...init });
|
|
51
|
-
const text = await response.text();
|
|
52
|
-
const payload = text ? JSON.parse(text) : {};
|
|
53
|
-
if (!response.ok) {
|
|
54
|
-
const message = typeof payload?.error === "string" ? payload.error : `Request failed: ${response.status}`;
|
|
55
|
-
throw new Error(message);
|
|
56
|
-
}
|
|
57
|
-
return payload;
|
|
58
|
-
}
|
|
59
|
-
async function fetchOptionalJson(path, init) {
|
|
60
|
-
try {
|
|
61
|
-
return await fetchJson(path, init);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function asNumber(value) {
|
|
68
|
-
if (typeof value === "number" && Number.isFinite(value))
|
|
69
|
-
return value;
|
|
70
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
71
|
-
const numeric = Number(value.replace(/,/g, ""));
|
|
72
|
-
if (Number.isFinite(numeric))
|
|
73
|
-
return numeric;
|
|
74
|
-
}
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
function asFiniteNumber(value, fallback = 0) {
|
|
78
|
-
if (typeof value === "number" && Number.isFinite(value))
|
|
79
|
-
return value;
|
|
80
|
-
if (typeof value === "string") {
|
|
81
|
-
const numeric = Number(value);
|
|
82
|
-
if (Number.isFinite(numeric))
|
|
83
|
-
return numeric;
|
|
84
|
-
}
|
|
85
|
-
return fallback;
|
|
86
|
-
}
|
|
87
|
-
function clamp(value, min, max) {
|
|
88
|
-
return Math.max(min, Math.min(max, value));
|
|
89
|
-
}
|
|
90
|
-
function formatValue(value, format = "compact") {
|
|
91
|
-
const numeric = asNumber(value);
|
|
92
|
-
if (numeric == null)
|
|
93
|
-
return value == null ? "n/a" : String(value);
|
|
94
|
-
if (format === "percent") {
|
|
95
|
-
return new Intl.NumberFormat(undefined, {
|
|
96
|
-
maximumFractionDigits: 1,
|
|
97
|
-
style: "percent"
|
|
98
|
-
}).format(numeric);
|
|
99
|
-
}
|
|
100
|
-
if (format === "currency") {
|
|
101
|
-
return new Intl.NumberFormat(undefined, {
|
|
102
|
-
currency: "USD",
|
|
103
|
-
maximumFractionDigits: 2,
|
|
104
|
-
notation: "compact",
|
|
105
|
-
style: "currency"
|
|
106
|
-
}).format(numeric);
|
|
107
|
-
}
|
|
108
|
-
if (format === "number") {
|
|
109
|
-
return new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(numeric);
|
|
110
|
-
}
|
|
111
|
-
return new Intl.NumberFormat(undefined, {
|
|
112
|
-
maximumFractionDigits: 2,
|
|
113
|
-
notation: "compact"
|
|
114
|
-
}).format(numeric);
|
|
115
|
-
}
|
|
116
|
-
function cardLabel(card) {
|
|
117
|
-
return cardMetrics(card)[0]?.label ?? card.id;
|
|
118
|
-
}
|
|
119
|
-
function rowMatchesFilter(row, filter) {
|
|
120
|
-
if (!filter)
|
|
121
|
-
return true;
|
|
122
|
-
return Object.entries(filter).every(([field, expected]) => String(row[field] ?? "") === String(expected ?? ""));
|
|
123
|
-
}
|
|
124
|
-
function movementDirection(value) {
|
|
125
|
-
if (typeof value !== "string")
|
|
126
|
-
return null;
|
|
127
|
-
const trimmed = value.trim();
|
|
128
|
-
if (!trimmed)
|
|
129
|
-
return null;
|
|
130
|
-
if (/^[+↑]/.test(trimmed))
|
|
131
|
-
return "positive";
|
|
132
|
-
if (/^[-−↓]/.test(trimmed))
|
|
133
|
-
return "negative";
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
function formatMetricValue(value, metric) {
|
|
137
|
-
if (value == null || value === "")
|
|
138
|
-
return null;
|
|
139
|
-
const numeric = asNumber(value);
|
|
140
|
-
const rendered = formatValue(value, metric.format);
|
|
141
|
-
if (!metric.signed || numeric == null || numeric === 0)
|
|
142
|
-
return rendered;
|
|
143
|
-
return `${numeric > 0 ? "+" : ""}${rendered}`;
|
|
144
|
-
}
|
|
145
|
-
function metricMovementDirection(value, signed = false) {
|
|
146
|
-
if (!signed)
|
|
147
|
-
return null;
|
|
148
|
-
const numeric = asNumber(value);
|
|
149
|
-
if (numeric != null) {
|
|
150
|
-
if (numeric > 0)
|
|
151
|
-
return "positive";
|
|
152
|
-
if (numeric < 0)
|
|
153
|
-
return "negative";
|
|
154
|
-
return "neutral";
|
|
155
|
-
}
|
|
156
|
-
return movementDirection(value);
|
|
157
|
-
}
|
|
158
|
-
function cardMetrics(card) {
|
|
159
|
-
return Array.isArray(card.metrics) ? card.metrics : [];
|
|
160
|
-
}
|
|
161
|
-
function MetricInfoIcon({ className }) {
|
|
162
|
-
return (<svg aria-hidden="true" className={className} fill="none" height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg">
|
|
163
|
-
<path d="M10.6 9.70459C11.0142 9.70461 11.35 10.0404 11.35 10.4546V13.7876C11.35 14.2018 11.0142 14.5376 10.6 14.5376C10.1858 14.5376 9.84998 14.2018 9.84998 13.7876V10.4546C9.84998 10.0404 10.1858 9.70459 10.6 9.70459Z" fill="currentColor"/>
|
|
164
|
-
<path d="M10.6 6.2876C11.1292 6.28762 11.558 6.71732 11.558 7.24658C11.5578 7.77569 11.1291 8.20457 10.6 8.20459C10.0708 8.20459 9.64215 7.7757 9.64197 7.24658C9.64197 6.71731 10.0707 6.2876 10.6 6.2876Z" fill="currentColor"/>
|
|
165
|
-
<path clipRule="evenodd" d="M10.6 2.53955C14.9713 2.53955 18.515 6.08326 18.515 10.4546C18.515 14.8259 14.9713 18.3696 10.6 18.3696C6.22864 18.3696 2.68494 14.8259 2.68494 10.4546C2.68494 6.08326 6.22864 2.53955 10.6 2.53955ZM10.6 3.86963C6.96318 3.86963 4.01501 6.81779 4.01501 10.4546C4.01501 14.0914 6.96318 17.0396 10.6 17.0396C14.2368 17.0396 17.1849 14.0914 17.1849 10.4546C17.1849 6.81779 14.2368 3.86963 10.6 3.86963Z" fill="currentColor" fillRule="evenodd"/>
|
|
166
|
-
</svg>);
|
|
167
|
-
}
|
|
168
|
-
function MetricDescriptionPopover({ description, label }) {
|
|
169
|
-
const tooltipId = useId();
|
|
170
|
-
const buttonRef = useRef(null);
|
|
171
|
-
const popoverRef = useRef(null);
|
|
172
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
173
|
-
const [popoverStyle, setPopoverStyle] = useState(null);
|
|
174
|
-
const updatePopoverPosition = useCallback(() => {
|
|
175
|
-
if (typeof window === "undefined")
|
|
176
|
-
return;
|
|
177
|
-
const anchor = buttonRef.current?.getBoundingClientRect();
|
|
178
|
-
if (!anchor)
|
|
179
|
-
return;
|
|
180
|
-
const margin = 12;
|
|
181
|
-
const gap = 8;
|
|
182
|
-
const maxWidth = Math.max(120, Math.min(320, window.innerWidth - margin * 2));
|
|
183
|
-
const popoverRect = popoverRef.current?.getBoundingClientRect();
|
|
184
|
-
const width = Math.min(popoverRect?.width ?? maxWidth, maxWidth);
|
|
185
|
-
const height = popoverRect?.height ?? 0;
|
|
186
|
-
let left = anchor.left + anchor.width / 2 - width / 2;
|
|
187
|
-
left = clamp(left, margin, window.innerWidth - margin - width);
|
|
188
|
-
let top = anchor.bottom + gap;
|
|
189
|
-
if (height && top + height > window.innerHeight - margin && anchor.top - height - gap >= margin) {
|
|
190
|
-
top = anchor.top - height - gap;
|
|
191
|
-
}
|
|
192
|
-
else if (height) {
|
|
193
|
-
top = clamp(top, margin, window.innerHeight - margin - height);
|
|
194
|
-
}
|
|
195
|
-
setPopoverStyle((current) => {
|
|
196
|
-
if (current?.left === left && current?.top === top && current?.maxWidth === maxWidth)
|
|
197
|
-
return current;
|
|
198
|
-
return { left, maxWidth, top };
|
|
199
|
-
});
|
|
200
|
-
}, []);
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
if (!isOpen) {
|
|
203
|
-
setPopoverStyle(null);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
updatePopoverPosition();
|
|
207
|
-
const frame = window.requestAnimationFrame(updatePopoverPosition);
|
|
208
|
-
window.addEventListener("resize", updatePopoverPosition);
|
|
209
|
-
window.addEventListener("scroll", updatePopoverPosition, true);
|
|
210
|
-
return () => {
|
|
211
|
-
window.cancelAnimationFrame(frame);
|
|
212
|
-
window.removeEventListener("resize", updatePopoverPosition);
|
|
213
|
-
window.removeEventListener("scroll", updatePopoverPosition, true);
|
|
214
|
-
};
|
|
215
|
-
}, [isOpen, updatePopoverPosition]);
|
|
216
|
-
const renderedPopoverStyle = popoverStyle
|
|
217
|
-
? { left: popoverStyle.left, maxWidth: popoverStyle.maxWidth, top: popoverStyle.top }
|
|
218
|
-
: { left: 0, maxWidth: "min(320px, calc(100vw - 24px))", top: 0, visibility: "hidden" };
|
|
219
|
-
return (<span className="kpi-info-wrap" onBlur={() => setIsOpen(false)} onFocus={() => setIsOpen(true)} onMouseEnter={() => setIsOpen(true)} onMouseLeave={() => setIsOpen(false)}>
|
|
220
|
-
<button aria-describedby={isOpen ? tooltipId : undefined} aria-label={`${label}: ${description}`} className="kpi-info" ref={buttonRef} type="button">
|
|
221
|
-
<MetricInfoIcon className="kpi-info-icon"/>
|
|
222
|
-
</button>
|
|
223
|
-
{isOpen && typeof document !== "undefined" ? createPortal(<span className="kpi-info-popover" id={tooltipId} ref={popoverRef} role="tooltip" style={renderedPopoverStyle}>
|
|
224
|
-
{description}
|
|
225
|
-
</span>, document.body) : null}
|
|
226
|
-
</span>);
|
|
227
|
-
}
|
|
228
|
-
function tableColumnFormat(column) {
|
|
229
|
-
if (column.format)
|
|
230
|
-
return column.format;
|
|
231
|
-
return column.type === "currency" || column.type === "number" || column.type === "percent"
|
|
232
|
-
? column.type
|
|
233
|
-
: undefined;
|
|
234
|
-
}
|
|
235
|
-
function tableColumnLooksLikeMovement(column) {
|
|
236
|
-
return column.movement === true || column.semantic === "movement" || column.role === "movement";
|
|
237
|
-
}
|
|
238
|
-
function tableCellMovementClass(column, value) {
|
|
239
|
-
if (!tableColumnLooksLikeMovement(column))
|
|
240
|
-
return "";
|
|
241
|
-
const numeric = asNumber(value);
|
|
242
|
-
const direction = numeric == null ? movementDirection(value) : numeric > 0 ? "positive" : numeric < 0 ? "negative" : "neutral";
|
|
243
|
-
if (!direction || direction === "neutral")
|
|
244
|
-
return "";
|
|
245
|
-
return `table-cell-movement table-cell-movement-${direction}`;
|
|
246
|
-
}
|
|
247
|
-
function formatTableDate(value) {
|
|
248
|
-
if (value == null || value === "")
|
|
249
|
-
return "";
|
|
250
|
-
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
251
|
-
return new Intl.DateTimeFormat(undefined, { dateStyle: "medium" }).format(value);
|
|
252
|
-
}
|
|
253
|
-
if (typeof value !== "string")
|
|
254
|
-
return String(value);
|
|
255
|
-
const trimmed = value.trim();
|
|
256
|
-
const monthMatch = trimmed.match(/^(\d{4})-(\d{2})$/);
|
|
257
|
-
if (monthMatch) {
|
|
258
|
-
const [, yearValue, monthValue] = monthMatch;
|
|
259
|
-
const date = new Date(Date.UTC(Number(yearValue), Number(monthValue) - 1, 1));
|
|
260
|
-
if (!Number.isNaN(date.getTime())) {
|
|
261
|
-
return new Intl.DateTimeFormat(undefined, {
|
|
262
|
-
month: "short",
|
|
263
|
-
timeZone: "UTC",
|
|
264
|
-
year: "numeric"
|
|
265
|
-
}).format(date);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
const dayMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})(?:$|[T\s])/);
|
|
269
|
-
if (dayMatch) {
|
|
270
|
-
const [, yearValue, monthValue, dayValue] = dayMatch;
|
|
271
|
-
const date = new Date(Date.UTC(Number(yearValue), Number(monthValue) - 1, Number(dayValue)));
|
|
272
|
-
if (!Number.isNaN(date.getTime())) {
|
|
273
|
-
return new Intl.DateTimeFormat(undefined, {
|
|
274
|
-
day: "numeric",
|
|
275
|
-
month: "short",
|
|
276
|
-
timeZone: "UTC",
|
|
277
|
-
year: "numeric"
|
|
278
|
-
}).format(date);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return trimmed;
|
|
282
|
-
}
|
|
283
|
-
function formatTableCellValue(column, value) {
|
|
284
|
-
if (column.type === "date")
|
|
285
|
-
return formatTableDate(value);
|
|
286
|
-
const format = tableColumnFormat(column);
|
|
287
|
-
const rendered = format ? formatValue(value, format) : String(value ?? "");
|
|
288
|
-
if (!tableColumnLooksLikeMovement(column))
|
|
289
|
-
return rendered;
|
|
290
|
-
const numeric = asNumber(value);
|
|
291
|
-
if (numeric == null || numeric === 0)
|
|
292
|
-
return rendered;
|
|
293
|
-
return `${numeric > 0 ? "+" : ""}${rendered}`;
|
|
294
|
-
}
|
|
295
|
-
function normalizedTableTextLength(value) {
|
|
296
|
-
return String(value ?? "").replace(/\s+/g, " ").trim().length;
|
|
297
|
-
}
|
|
298
|
-
function percentileValue(values, percentile) {
|
|
299
|
-
if (!values.length)
|
|
300
|
-
return 0;
|
|
301
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
302
|
-
const index = clamp(Math.ceil(sorted.length * percentile) - 1, 0, sorted.length - 1);
|
|
303
|
-
return sorted[index];
|
|
304
|
-
}
|
|
305
|
-
function isNumericTableColumn(column, rows) {
|
|
306
|
-
if (tableColumnFormat(column))
|
|
307
|
-
return true;
|
|
308
|
-
let observed = false;
|
|
309
|
-
for (const row of rows.slice(0, TABLE_COLUMN_SAMPLE_SIZE)) {
|
|
310
|
-
const value = row[column.field];
|
|
311
|
-
if (value == null || value === "")
|
|
312
|
-
continue;
|
|
313
|
-
observed = true;
|
|
314
|
-
if (asNumber(value) == null)
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
return observed;
|
|
318
|
-
}
|
|
319
|
-
function tableColumnDisplayText(column, row) {
|
|
320
|
-
return formatTableCellValue(column, row[column.field]);
|
|
321
|
-
}
|
|
322
|
-
function estimateTableColumnWidth(column, rows, density) {
|
|
323
|
-
const sampleRows = rows.slice(0, TABLE_COLUMN_SAMPLE_SIZE);
|
|
324
|
-
const labelLength = normalizedTableTextLength(column.label);
|
|
325
|
-
const cellLengths = sampleRows
|
|
326
|
-
.map((row) => normalizedTableTextLength(tableColumnDisplayText(column, row)))
|
|
327
|
-
.filter((length) => length > 0);
|
|
328
|
-
const p90Length = percentileValue([labelLength, ...cellLengths], 0.9);
|
|
329
|
-
const maxLength = Math.max(labelLength, ...cellLengths, 0);
|
|
330
|
-
const columnKey = `${column.field} ${column.label}`.toLowerCase();
|
|
331
|
-
const isLongTextColumn = /(comment|description|detail|explanation|insight|interpretation|note|reason|summary)/.test(columnKey)
|
|
332
|
-
|| maxLength > 34;
|
|
333
|
-
const isNumericColumn = isNumericTableColumn(column, sampleRows);
|
|
334
|
-
const isDateColumn = /(date|day|month|quarter|week)/.test(columnKey);
|
|
335
|
-
const horizontalPadding = TABLE_COLUMN_HEADER_CHROME_WIDTH + (density === "dense" ? TABLE_COLUMN_DENSE_HORIZONTAL_PADDING : 0);
|
|
336
|
-
const charWidth = isNumericColumn ? 7.2 : 7.4;
|
|
337
|
-
const targetLength = isLongTextColumn
|
|
338
|
-
? Math.min(Math.max(labelLength + 4, p90Length), 44)
|
|
339
|
-
: Math.max(labelLength, p90Length);
|
|
340
|
-
const measuredWidth = Math.ceil(targetLength * charWidth + horizontalPadding);
|
|
341
|
-
if (isNumericColumn) {
|
|
342
|
-
const minWidth = tableColumnFormat(column) === "percent" ? 96 : 108;
|
|
343
|
-
return clamp(measuredWidth, minWidth, TABLE_COLUMN_NUMERIC_MAX_WIDTH);
|
|
344
|
-
}
|
|
345
|
-
if (isDateColumn) {
|
|
346
|
-
return clamp(measuredWidth, 116, 156);
|
|
347
|
-
}
|
|
348
|
-
if (isLongTextColumn) {
|
|
349
|
-
return clamp(measuredWidth, 220, TABLE_COLUMN_LONG_TEXT_MAX_WIDTH);
|
|
350
|
-
}
|
|
351
|
-
return clamp(measuredWidth, density === "dense" ? 124 : 112, TABLE_COLUMN_TEXT_MAX_WIDTH);
|
|
352
|
-
}
|
|
353
|
-
function estimateTableColumnWidths(table, rows, density) {
|
|
354
|
-
return Object.fromEntries(table.columns.map((column) => [
|
|
355
|
-
column.field,
|
|
356
|
-
estimateTableColumnWidth(column, rows, density)
|
|
357
|
-
]));
|
|
358
|
-
}
|
|
359
|
-
function formatDate(value) {
|
|
360
|
-
if (!value)
|
|
361
|
-
return "Unknown";
|
|
362
|
-
const date = new Date(value);
|
|
363
|
-
if (Number.isNaN(date.getTime()))
|
|
364
|
-
return value;
|
|
365
|
-
return new Intl.DateTimeFormat(undefined, {
|
|
366
|
-
dateStyle: "medium",
|
|
367
|
-
timeStyle: "short"
|
|
368
|
-
}).format(date);
|
|
369
|
-
}
|
|
370
|
-
function snapshotStatusLabel(status) {
|
|
371
|
-
if (!status || status === "ready")
|
|
372
|
-
return null;
|
|
373
|
-
if (status === "fixture")
|
|
374
|
-
return "Fixture data";
|
|
375
|
-
if (status === "partial")
|
|
376
|
-
return null;
|
|
377
|
-
return "Blocked snapshot";
|
|
378
|
-
}
|
|
379
|
-
function getRows(snapshot, dataset) {
|
|
380
|
-
return snapshot?.datasets?.[dataset] ?? [];
|
|
381
|
-
}
|
|
382
|
-
function asArray(value) {
|
|
383
|
-
return Array.isArray(value) ? value : [];
|
|
384
|
-
}
|
|
385
|
-
function asRecord(value) {
|
|
386
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
387
|
-
? value
|
|
388
|
-
: {};
|
|
389
|
-
}
|
|
390
|
-
function normalizeManifest(rawManifest) {
|
|
391
|
-
const manifest = asRecord(rawManifest);
|
|
392
|
-
return {
|
|
393
|
-
...manifest,
|
|
394
|
-
version: 1,
|
|
395
|
-
title: typeof manifest.title === "string" ? manifest.title : "Data Analytics artifact",
|
|
396
|
-
generatedAt: typeof manifest.generatedAt === "string" ? manifest.generatedAt : "",
|
|
397
|
-
filters: asArray(manifest.filters).map((rawFilter) => {
|
|
398
|
-
const filter = asRecord(rawFilter);
|
|
399
|
-
return {
|
|
400
|
-
...filter,
|
|
401
|
-
targets: asArray(filter.targets)
|
|
402
|
-
};
|
|
403
|
-
}),
|
|
404
|
-
cards: asArray(manifest.cards).map((rawCard) => asRecord(rawCard)),
|
|
405
|
-
charts: asArray(manifest.charts).map((rawChart) => {
|
|
406
|
-
const chart = asRecord(rawChart);
|
|
407
|
-
return {
|
|
408
|
-
...chart,
|
|
409
|
-
referenceLines: asArray(chart.referenceLines)
|
|
410
|
-
};
|
|
411
|
-
}),
|
|
412
|
-
tables: asArray(manifest.tables).map((rawTable) => {
|
|
413
|
-
const table = asRecord(rawTable);
|
|
414
|
-
return {
|
|
415
|
-
...table,
|
|
416
|
-
columns: asArray(table.columns)
|
|
417
|
-
};
|
|
418
|
-
}),
|
|
419
|
-
sources: asArray(manifest.sources).map((rawSource) => asRecord(rawSource)),
|
|
420
|
-
blocks: asArray(manifest.blocks).map((rawBlock) => {
|
|
421
|
-
const block = asRecord(rawBlock);
|
|
422
|
-
return {
|
|
423
|
-
...block,
|
|
424
|
-
cardIds: asArray(block.cardIds)
|
|
425
|
-
};
|
|
426
|
-
})
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
function normalizeSnapshot(rawSnapshot) {
|
|
430
|
-
const snapshot = asRecord(rawSnapshot);
|
|
431
|
-
const datasets = {};
|
|
432
|
-
const rawDatasets = asRecord(snapshot.datasets);
|
|
433
|
-
for (const [dataset, rows] of Object.entries(rawDatasets)) {
|
|
434
|
-
datasets[dataset] = asArray(rows).filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
|
|
435
|
-
}
|
|
436
|
-
return {
|
|
437
|
-
...snapshot,
|
|
438
|
-
version: 1,
|
|
439
|
-
generatedAt: typeof snapshot.generatedAt === "string" ? snapshot.generatedAt : "",
|
|
440
|
-
datasets,
|
|
441
|
-
accessIssues: asArray(snapshot.accessIssues)
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
function filterTargets(filter) {
|
|
445
|
-
const targets = [{ dataset: filter.dataset, field: filter.field }];
|
|
446
|
-
for (const target of filter.targets ?? []) {
|
|
447
|
-
targets.push({ dataset: target.dataset, field: target.field ?? filter.field });
|
|
448
|
-
}
|
|
449
|
-
const seen = new Set();
|
|
450
|
-
return targets.filter((target) => {
|
|
451
|
-
const key = `${target.dataset}\u0001${target.field}`;
|
|
452
|
-
if (seen.has(key))
|
|
453
|
-
return false;
|
|
454
|
-
seen.add(key);
|
|
455
|
-
return true;
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
function filterFieldForDataset(filter, dataset) {
|
|
459
|
-
return filterTargets(filter).find((target) => target.dataset === dataset)?.field ?? null;
|
|
460
|
-
}
|
|
461
|
-
function dashboardSurfaceDatasets(cards, charts, tables) {
|
|
462
|
-
return new Set([...cards, ...charts, ...tables]
|
|
463
|
-
.map((surface) => surface.dataset)
|
|
464
|
-
.filter((dataset) => Boolean(dataset)));
|
|
465
|
-
}
|
|
466
|
-
function isGlobalFilter(filter, cards, charts, tables) {
|
|
467
|
-
const requiredDatasets = dashboardSurfaceDatasets(cards, charts, tables);
|
|
468
|
-
if (requiredDatasets.size <= 1)
|
|
469
|
-
return true;
|
|
470
|
-
const targetDatasets = new Set(filterTargets(filter).map((target) => target.dataset));
|
|
471
|
-
return [...requiredDatasets].every((dataset) => targetDatasets.has(dataset));
|
|
472
|
-
}
|
|
473
|
-
function getGlobalFilters(filters, cards, charts, tables) {
|
|
474
|
-
return filters.filter((filter) => isGlobalFilter(filter, cards, charts, tables));
|
|
475
|
-
}
|
|
476
|
-
function filterRowsForDataset(rows, dataset, filters, selectedFilters, usedFields = []) {
|
|
477
|
-
const usedFieldSet = new Set(usedFields.filter(Boolean));
|
|
478
|
-
const explicitAllFields = new Set(filters
|
|
479
|
-
.map((filter) => {
|
|
480
|
-
const field = filterFieldForDataset(filter, dataset);
|
|
481
|
-
const selected = selectedFilters[filter.id] ?? filter.defaultValue ?? "all";
|
|
482
|
-
return field && selected === "all" && rows.some((row) => String(row[field] ?? "") === "all")
|
|
483
|
-
? field
|
|
484
|
-
: null;
|
|
485
|
-
})
|
|
486
|
-
.filter((field) => Boolean(field)));
|
|
487
|
-
const filteredRows = rows.filter((row) => filters.every((filter) => {
|
|
488
|
-
const field = filterFieldForDataset(filter, dataset);
|
|
489
|
-
if (!field)
|
|
490
|
-
return true;
|
|
491
|
-
const selected = selectedFilters[filter.id] ?? filter.defaultValue ?? "all";
|
|
492
|
-
if (selected === "all") {
|
|
493
|
-
return !explicitAllFields.has(field) || String(row[field] ?? "") === "all";
|
|
494
|
-
}
|
|
495
|
-
return String(row[field] ?? "") === selected;
|
|
496
|
-
}));
|
|
497
|
-
const aggregateFields = Object.keys(rows[0] ?? {}).filter((field) => {
|
|
498
|
-
if (usedFieldSet.has(field))
|
|
499
|
-
return false;
|
|
500
|
-
let hasAggregate = false;
|
|
501
|
-
let hasBreakdown = false;
|
|
502
|
-
for (const row of filteredRows) {
|
|
503
|
-
const value = String(row[field] ?? "");
|
|
504
|
-
if (value === "all")
|
|
505
|
-
hasAggregate = true;
|
|
506
|
-
else if (value)
|
|
507
|
-
hasBreakdown = true;
|
|
508
|
-
if (hasAggregate && hasBreakdown)
|
|
509
|
-
return true;
|
|
510
|
-
}
|
|
511
|
-
return false;
|
|
512
|
-
});
|
|
513
|
-
if (!aggregateFields.length)
|
|
514
|
-
return filteredRows;
|
|
515
|
-
const aggregateRows = filteredRows.filter((row) => aggregateFields.every((field) => String(row[field] ?? "") === "all"));
|
|
516
|
-
return aggregateRows.length ? aggregateRows : filteredRows;
|
|
517
|
-
}
|
|
518
|
-
function DashboardShell({ children, detailMode = false, isEditMode = false, surface }) {
|
|
519
|
-
return (<main className={`dashboard-shell ${surface === "report" ? "report-shell" : ""} ${detailMode ? "chart-detail-shell" : ""} ${isEditMode ? "is-app-editing" : ""}`.trim()} data-edit-mode={isEditMode ? "true" : "false"}>
|
|
520
|
-
{children}
|
|
521
|
-
</main>);
|
|
522
|
-
}
|
|
523
|
-
function dashboardCardLayout(layout) {
|
|
524
|
-
return layout ?? DEFAULT_DASHBOARD_CARD_LAYOUT;
|
|
525
|
-
}
|
|
526
|
-
function reportCardLayout(layout) {
|
|
527
|
-
return layout ?? DEFAULT_REPORT_CARD_LAYOUT;
|
|
528
|
-
}
|
|
529
|
-
function contentLayoutKey(manifest) {
|
|
530
|
-
if (!manifest)
|
|
531
|
-
return null;
|
|
532
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
533
|
-
return `datascience-dashboard:content-layout:${base}`;
|
|
534
|
-
}
|
|
535
|
-
function chartTextKey(manifest) {
|
|
536
|
-
if (!manifest)
|
|
537
|
-
return null;
|
|
538
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
539
|
-
return `datascience-dashboard:chart-text:${base}`;
|
|
540
|
-
}
|
|
541
|
-
function chartTypeKey(manifest) {
|
|
542
|
-
if (!manifest)
|
|
543
|
-
return null;
|
|
544
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
545
|
-
return `datascience-${manifest.surface ?? "dashboard"}:chart-type:${base}`;
|
|
546
|
-
}
|
|
547
|
-
function chartSpecKey(manifest) {
|
|
548
|
-
if (!manifest)
|
|
549
|
-
return null;
|
|
550
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
551
|
-
return `datascience-${manifest.surface ?? "dashboard"}:chart-spec:${base}`;
|
|
552
|
-
}
|
|
553
|
-
function pageTitleTextKey(manifest) {
|
|
554
|
-
if (!manifest)
|
|
555
|
-
return null;
|
|
556
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
557
|
-
return `datascience-${manifest.surface ?? "dashboard"}:page-title:${base}`;
|
|
558
|
-
}
|
|
559
|
-
function tableTextKey(manifest) {
|
|
560
|
-
if (!manifest)
|
|
561
|
-
return null;
|
|
562
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
563
|
-
return `datascience-${manifest.surface ?? "dashboard"}:table-text:${base}`;
|
|
564
|
-
}
|
|
565
|
-
function storageKeyHash(value) {
|
|
566
|
-
let hash = 2166136261;
|
|
567
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
568
|
-
hash ^= value.charCodeAt(index);
|
|
569
|
-
hash = Math.imul(hash, 16777619);
|
|
570
|
-
}
|
|
571
|
-
return (hash >>> 0).toString(36);
|
|
572
|
-
}
|
|
573
|
-
function blockTextKey(manifest) {
|
|
574
|
-
if (!manifest)
|
|
575
|
-
return null;
|
|
576
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
577
|
-
const blockSignature = (manifest.blocks ?? [])
|
|
578
|
-
.map((block) => `${block.id}:${block.type}:${typeof block.body === "string" ? storageKeyHash(block.body) : ""}`)
|
|
579
|
-
.join(",");
|
|
580
|
-
return `datascience-report:block-text:${base}:${storageKeyHash(blockSignature)}`;
|
|
581
|
-
}
|
|
582
|
-
function deletedReportBlocksKey(manifest) {
|
|
583
|
-
if (!manifest)
|
|
584
|
-
return null;
|
|
585
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
586
|
-
const blockSignature = (manifest.blocks ?? [])
|
|
587
|
-
.map((block) => block.id)
|
|
588
|
-
.join(",");
|
|
589
|
-
return `datascience-report:deleted-blocks:${base}:${blockSignature}`;
|
|
590
|
-
}
|
|
591
|
-
function tableColumnWidthKey(manifest) {
|
|
592
|
-
if (!manifest)
|
|
593
|
-
return null;
|
|
594
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
595
|
-
const surface = manifest.surface ?? "dashboard";
|
|
596
|
-
const tableSignature = (manifest.tables ?? [])
|
|
597
|
-
.map((table) => `${table.id}:${table.columns.map((column) => column.field).join("|")}`)
|
|
598
|
-
.join(",");
|
|
599
|
-
return `datascience-${surface}:table-column-widths:${base}:${tableSignature}`;
|
|
600
|
-
}
|
|
601
|
-
function reportContentLayoutKey(manifest) {
|
|
602
|
-
if (!manifest)
|
|
603
|
-
return null;
|
|
604
|
-
const base = `${manifest.title}:${manifest.generatedAt}`;
|
|
605
|
-
const blockSignature = (manifest.blocks ?? [])
|
|
606
|
-
.map((block) => `${block.id}:${reportCardLayout(block.layout)}`)
|
|
607
|
-
.join(",");
|
|
608
|
-
return `datascience-report:content-layout:v3:${base}:${blockSignature}`;
|
|
609
|
-
}
|
|
610
|
-
function sourceForChart(chart, sources) {
|
|
611
|
-
if (chart?.source && typeof chart.source === "object")
|
|
612
|
-
return chart.source;
|
|
613
|
-
return sources.find((source) => source.id === chart.sourceId) ?? null;
|
|
614
|
-
}
|
|
615
|
-
function sourceForTable(table, sources) {
|
|
616
|
-
if (table?.source && typeof table.source === "object")
|
|
617
|
-
return table.source;
|
|
618
|
-
return sources.find((source) => source.id === table.sourceId) ?? null;
|
|
619
|
-
}
|
|
620
|
-
function sourceQueryFromChartSpec(chart) {
|
|
621
|
-
return chart?.source?.query ?? null;
|
|
622
|
-
}
|
|
623
|
-
function sourceQueryFromSourceSpec(source) {
|
|
624
|
-
if (!source)
|
|
625
|
-
return null;
|
|
626
|
-
return source.query ?? null;
|
|
627
|
-
}
|
|
628
|
-
function queryTextFromSourceQuery(sourceQuery) {
|
|
629
|
-
return (sourceQuery?.sql ?? "").trim();
|
|
630
|
-
}
|
|
631
|
-
function accessIssueForChart(chart, issues) {
|
|
632
|
-
return (issues.find((issue) => issue.dataset === chart.dataset) ??
|
|
633
|
-
issues.find((issue) => issue.sourceId && issue.sourceId === chart.sourceId) ??
|
|
634
|
-
null);
|
|
635
|
-
}
|
|
636
|
-
function activeFilterSummary(filters, selectedFilters) {
|
|
637
|
-
const active = filters
|
|
638
|
-
.map((filter) => {
|
|
639
|
-
const value = selectedFilters[filter.id] ?? filter.defaultValue ?? "all";
|
|
640
|
-
return value === "all" ? null : `${filter.label}: ${value}`;
|
|
641
|
-
})
|
|
642
|
-
.filter(Boolean);
|
|
643
|
-
return active.length ? active.join(", ") : "None";
|
|
644
|
-
}
|
|
645
|
-
function stringListFromValue(value) {
|
|
646
|
-
if (Array.isArray(value)) {
|
|
647
|
-
return value
|
|
648
|
-
.map((item) => {
|
|
649
|
-
if (item && typeof item === "object") {
|
|
650
|
-
const label = item.label ?? item.name ?? item.table ?? item.field ?? item.metric ?? item.id;
|
|
651
|
-
const detail = item.description ?? item.definition ?? item.value ?? item.expression;
|
|
652
|
-
return label && detail ? `${label}: ${detail}` : label ?? detail;
|
|
653
|
-
}
|
|
654
|
-
return item;
|
|
655
|
-
})
|
|
656
|
-
.map((item) => String(item ?? "").trim())
|
|
657
|
-
.filter(Boolean);
|
|
658
|
-
}
|
|
659
|
-
if (value && typeof value === "object") {
|
|
660
|
-
return Object.entries(value)
|
|
661
|
-
.map(([key, item]) => `${key}: ${item}`)
|
|
662
|
-
.map((item) => item.trim())
|
|
663
|
-
.filter(Boolean);
|
|
664
|
-
}
|
|
665
|
-
const textValue = String(value ?? "").trim();
|
|
666
|
-
return textValue ? [textValue] : [];
|
|
667
|
-
}
|
|
668
|
-
function firstStringList(objects, keys) {
|
|
669
|
-
for (const object of objects) {
|
|
670
|
-
if (!object)
|
|
671
|
-
continue;
|
|
672
|
-
for (const key of keys) {
|
|
673
|
-
const values = stringListFromValue(object[key]);
|
|
674
|
-
if (values.length)
|
|
675
|
-
return values;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
return [];
|
|
679
|
-
}
|
|
680
|
-
function isLikelySourceTableName(value) {
|
|
681
|
-
const tableName = String(value ?? "").trim().replace(/^[`"\[]|[`"\]]$/g, "");
|
|
682
|
-
return /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+){1,3}$/.test(tableName);
|
|
683
|
-
}
|
|
684
|
-
function sourceTableNamesFromMetadata(objects) {
|
|
685
|
-
const values = firstStringList(objects, ["tables_used", "tablesUsed", "source_tables", "sourceTables", "tables"]);
|
|
686
|
-
return values.filter(isLikelySourceTableName);
|
|
687
|
-
}
|
|
688
|
-
function extractTablesFromQuery(queryText) {
|
|
689
|
-
const tables = [];
|
|
690
|
-
const seen = new Set();
|
|
691
|
-
const pattern = /\b(?:from|join)\s+([`"\[]?[\w.-]+(?:\.[\w.-]+){0,3}[`"\]]?)/gi;
|
|
692
|
-
let match;
|
|
693
|
-
while ((match = pattern.exec(queryText ?? ""))) {
|
|
694
|
-
const table = match[1].replace(/^[`"\[]|[`"\]]$/g, "");
|
|
695
|
-
const normalized = table.toLowerCase();
|
|
696
|
-
if (!table || seen.has(normalized))
|
|
697
|
-
continue;
|
|
698
|
-
seen.add(normalized);
|
|
699
|
-
tables.push(table);
|
|
700
|
-
}
|
|
701
|
-
return tables;
|
|
702
|
-
}
|
|
703
|
-
function sourceBuildDetails({ activeFilters, columns = [], dataset, metrics = [], source, sourceQuery, snapshot }) {
|
|
704
|
-
const queryText = queryTextFromSourceQuery(sourceQueryFromSourceSpec(source)) || queryTextFromSourceQuery(sourceQuery) || "";
|
|
705
|
-
const metadataObjects = [source, sourceQueryFromSourceSpec(source), sourceQuery].filter(Boolean);
|
|
706
|
-
const explicitTables = sourceTableNamesFromMetadata(metadataObjects);
|
|
707
|
-
const inferredTables = extractTablesFromQuery(queryText);
|
|
708
|
-
const tables = explicitTables.length ? explicitTables : inferredTables;
|
|
709
|
-
const filters = firstStringList(metadataObjects, [
|
|
710
|
-
"filters",
|
|
711
|
-
"filter_descriptions",
|
|
712
|
-
"filterDescriptions",
|
|
713
|
-
"filter_description",
|
|
714
|
-
"filterDescription",
|
|
715
|
-
]);
|
|
716
|
-
const metricDefinitions = firstStringList(metadataObjects, [
|
|
717
|
-
"metric_definitions",
|
|
718
|
-
"metricDefinitions",
|
|
719
|
-
"metrics_definition",
|
|
720
|
-
"metricDefinition",
|
|
721
|
-
]);
|
|
722
|
-
const fallbackMetrics = metricDefinitions.length
|
|
723
|
-
? metricDefinitions
|
|
724
|
-
: metrics.length
|
|
725
|
-
? metrics.map((metric) => `${metric.label}: displayed from ${metric.field}`)
|
|
726
|
-
: columns
|
|
727
|
-
.filter((column) => ["number", "percent", "currency"].includes(column.format ?? column.type ?? ""))
|
|
728
|
-
.map((column) => `${column.label ?? column.field}: displayed from ${column.field}`)
|
|
729
|
-
.slice(0, 6);
|
|
730
|
-
const filterRows = filters.length ? filters : activeFilters && activeFilters !== "None" ? [activeFilters] : ["None declared"];
|
|
731
|
-
const tableRows = tables.length ? tables : ["Not declared"];
|
|
732
|
-
const snapshotValue = sourceQuery?.executed_at ?? sourceQueryFromSourceSpec(source)?.executed_at ?? snapshot?.generatedAt ?? "Not declared";
|
|
733
|
-
const primarySource = tableRows[0] === "Not declared" ? dataset ?? "reviewed rows" : tableRows[0];
|
|
734
|
-
const summary = `This block uses ${primarySource} for dataset ${dataset ?? "unknown"}.${filterRows[0] !== "None declared" ? ` Filters: ${filterRows.slice(0, 2).join("; ")}.` : ""} Use the source query below to inspect the exact logic.`;
|
|
735
|
-
return {
|
|
736
|
-
dataset: dataset ?? "Not declared",
|
|
737
|
-
fields: columns
|
|
738
|
-
.map((column) => column.field ?? column.key ?? column.label)
|
|
739
|
-
.filter(Boolean),
|
|
740
|
-
filters: filterRows,
|
|
741
|
-
metricDefinitions: fallbackMetrics.length ? fallbackMetrics : ["Displayed directly from source columns"],
|
|
742
|
-
snapshot: snapshotValue,
|
|
743
|
-
summary,
|
|
744
|
-
tables: tableRows
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
function detailText(values, fallback = "Not declared") {
|
|
748
|
-
const items = Array.isArray(values) ? values.filter(Boolean) : [values].filter(Boolean);
|
|
749
|
-
return items.length ? items.join("; ") : fallback;
|
|
750
|
-
}
|
|
751
|
-
function detailList(values, fallback = "Not declared") {
|
|
752
|
-
const items = Array.isArray(values) ? values.filter(Boolean) : [values].filter(Boolean);
|
|
753
|
-
if (!items.length)
|
|
754
|
-
return fallback;
|
|
755
|
-
return (<div className="source-detail-list">
|
|
756
|
-
{items.map((item, index) => <div className="source-detail-list-item" key={`${index}-${item}`}>
|
|
757
|
-
{item}
|
|
758
|
-
</div>)}
|
|
759
|
-
</div>);
|
|
760
|
-
}
|
|
761
|
-
function compareTableValues(a, b, field, direction) {
|
|
762
|
-
const aValue = a[field];
|
|
763
|
-
const bValue = b[field];
|
|
764
|
-
if (aValue == null && bValue == null)
|
|
765
|
-
return 0;
|
|
766
|
-
if (aValue == null)
|
|
767
|
-
return direction === "asc" ? 1 : -1;
|
|
768
|
-
if (bValue == null)
|
|
769
|
-
return direction === "asc" ? -1 : 1;
|
|
770
|
-
let result = 0;
|
|
771
|
-
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
772
|
-
result = aValue - bValue;
|
|
773
|
-
}
|
|
774
|
-
else {
|
|
775
|
-
result = String(aValue).localeCompare(String(bValue), undefined, {
|
|
776
|
-
numeric: true,
|
|
777
|
-
sensitivity: "base"
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
return direction === "asc" ? result : -result;
|
|
781
|
-
}
|
|
782
|
-
function MetricBadge({ metric, row }) {
|
|
783
|
-
const value = row[metric.field];
|
|
784
|
-
const renderedValue = formatMetricValue(value, metric);
|
|
785
|
-
if (!renderedValue)
|
|
786
|
-
return null;
|
|
787
|
-
const direction = metricMovementDirection(value, metric.signed === true);
|
|
788
|
-
return (<div className={`metric-badge ${direction ?? "neutral"}`}>
|
|
789
|
-
<span className="metric-badge-label">{metric.label}</span>
|
|
790
|
-
<span className="metric-badge-value">{renderedValue}</span>
|
|
791
|
-
</div>);
|
|
792
|
-
}
|
|
793
|
-
function KpiCard({ card, filters, selectedFilters, snapshot }) {
|
|
794
|
-
const metrics = cardMetrics(card);
|
|
795
|
-
const [primaryMetric, ...supportingMetrics] = metrics;
|
|
796
|
-
const rows = filterRowsForDataset(getRows(snapshot, card.dataset), card.dataset, filters, selectedFilters, metrics.map((metric) => metric.field));
|
|
797
|
-
const row = rows.find((candidate) => rowMatchesFilter(candidate, card.filter)) ?? {};
|
|
798
|
-
const label = cardLabel(card);
|
|
799
|
-
const description = card.description?.trim();
|
|
800
|
-
return (<section className="kpi-card">
|
|
801
|
-
<div className="kpi-label-row">
|
|
802
|
-
<div className="kpi-label">{label}</div>
|
|
803
|
-
{description ? <MetricDescriptionPopover description={description} label={label}/> : null}
|
|
804
|
-
</div>
|
|
805
|
-
<div className="kpi-value">{primaryMetric ? formatValue(row[primaryMetric.field], primaryMetric.format) : "—"}</div>
|
|
806
|
-
{supportingMetrics.length ? (<div className="metric-badge-row">
|
|
807
|
-
{supportingMetrics.map((metric) => (<MetricBadge key={`${metric.field}:${metric.label}`} metric={metric} row={row}/>))}
|
|
808
|
-
</div>) : null}
|
|
809
|
-
</section>);
|
|
810
|
-
}
|
|
811
|
-
function getKpiColumnCount(width, cardCount) {
|
|
812
|
-
const breakpoint = KPI_COLUMN_BREAKPOINTS.find((item) => width >= item.minWidth);
|
|
813
|
-
return Math.max(1, Math.min(cardCount, breakpoint?.columns ?? 1));
|
|
814
|
-
}
|
|
815
|
-
function useMeasuredElementSize() {
|
|
816
|
-
const ref = useRef(null);
|
|
817
|
-
const [size, setSize] = useState({ height: 0, width: 0 });
|
|
818
|
-
useEffect(() => {
|
|
819
|
-
const element = ref.current;
|
|
820
|
-
if (!element)
|
|
821
|
-
return;
|
|
822
|
-
let frame = 0;
|
|
823
|
-
const updateWidth = () => {
|
|
824
|
-
window.cancelAnimationFrame(frame);
|
|
825
|
-
frame = window.requestAnimationFrame(() => {
|
|
826
|
-
const rect = element.getBoundingClientRect();
|
|
827
|
-
setSize({
|
|
828
|
-
height: Math.floor(rect.height),
|
|
829
|
-
width: Math.floor(rect.width)
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
};
|
|
833
|
-
updateWidth();
|
|
834
|
-
const observer = new ResizeObserver(updateWidth);
|
|
835
|
-
observer.observe(element);
|
|
836
|
-
window.addEventListener("resize", updateWidth);
|
|
837
|
-
return () => {
|
|
838
|
-
window.cancelAnimationFrame(frame);
|
|
839
|
-
observer.disconnect();
|
|
840
|
-
window.removeEventListener("resize", updateWidth);
|
|
841
|
-
};
|
|
842
|
-
}, []);
|
|
843
|
-
return [ref, size];
|
|
844
|
-
}
|
|
845
|
-
function KpiStrip({ cards, filters, selectedFilters, snapshot }) {
|
|
846
|
-
const [stripRef, stripSize] = useMeasuredElementSize();
|
|
847
|
-
const fallbackWidth = typeof window === "undefined" ? 0 : window.innerWidth;
|
|
848
|
-
const columns = getKpiColumnCount(stripSize.width || fallbackWidth, cards.length);
|
|
849
|
-
const placeholderCount = (columns - (cards.length % columns)) % columns;
|
|
850
|
-
const stripStyle = { "--kpi-columns": columns };
|
|
851
|
-
if (!cards.length)
|
|
852
|
-
return null;
|
|
853
|
-
return (<section className={`kpi-strip kpi-columns-${columns}`} style={stripStyle} aria-label="Key metrics" ref={stripRef}>
|
|
854
|
-
{cards.map((card) => (<KpiCard card={card} filters={filters} key={card.id} selectedFilters={selectedFilters} snapshot={snapshot}/>))}
|
|
855
|
-
{Array.from({ length: placeholderCount }, (_, index) => (<div aria-hidden="true" className="kpi-card kpi-card-placeholder" key={`kpi-placeholder-${index}`}/>))}
|
|
856
|
-
</section>);
|
|
857
|
-
}
|
|
858
|
-
const MENU_CLOSE_ANIMATION_MS = 100;
|
|
859
|
-
const NARROW_FIXED_MENU_QUERY = "(max-width: 560px)";
|
|
860
|
-
function useDashboardMenu(isOpen, onOpenChange) {
|
|
861
|
-
const menuButtonRef = useRef(null);
|
|
862
|
-
const menuRef = useRef(null);
|
|
863
|
-
const isOpenRef = useRef(isOpen);
|
|
864
|
-
const [shouldRenderMenu, setShouldRenderMenu] = useState(isOpen);
|
|
865
|
-
const [menuMotionClass, setMenuMotionClass] = useState("opening");
|
|
866
|
-
const [fixedMenuStyle, setFixedMenuStyle] = useState(undefined);
|
|
867
|
-
useEffect(() => {
|
|
868
|
-
isOpenRef.current = isOpen;
|
|
869
|
-
}, [isOpen]);
|
|
870
|
-
const updateFixedMenuStyle = useCallback(() => {
|
|
871
|
-
if (typeof window === "undefined") {
|
|
872
|
-
return;
|
|
873
|
-
}
|
|
874
|
-
if (!window.matchMedia(NARROW_FIXED_MENU_QUERY).matches) {
|
|
875
|
-
setFixedMenuStyle(undefined);
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
const anchor = menuButtonRef.current?.getBoundingClientRect();
|
|
879
|
-
if (!anchor) {
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
const menuSurface = menuRef.current?.matches?.('[role="menu"]')
|
|
883
|
-
? menuRef.current
|
|
884
|
-
: menuRef.current?.querySelector?.('[role="menu"]');
|
|
885
|
-
const margin = 12;
|
|
886
|
-
const gap = 6;
|
|
887
|
-
const surfaceHeight = menuSurface?.getBoundingClientRect?.().height ?? 0;
|
|
888
|
-
let top = anchor.bottom + gap;
|
|
889
|
-
if (surfaceHeight && top + surfaceHeight > window.innerHeight - margin && anchor.top - surfaceHeight - gap >= margin) {
|
|
890
|
-
top = anchor.top - surfaceHeight - gap;
|
|
891
|
-
}
|
|
892
|
-
else if (surfaceHeight) {
|
|
893
|
-
top = clamp(top, margin, window.innerHeight - margin - surfaceHeight);
|
|
894
|
-
}
|
|
895
|
-
else {
|
|
896
|
-
top = clamp(top, margin, window.innerHeight - margin);
|
|
897
|
-
}
|
|
898
|
-
setFixedMenuStyle((current) => {
|
|
899
|
-
if (current?.top === top)
|
|
900
|
-
return current;
|
|
901
|
-
return { top };
|
|
902
|
-
});
|
|
903
|
-
}, []);
|
|
904
|
-
function menuItems() {
|
|
905
|
-
return Array.from(menuRef.current?.querySelectorAll('[role="menuitem"], [role="menuitemradio"]') ?? []).filter((item) => !item.disabled);
|
|
906
|
-
}
|
|
907
|
-
function focusMenuItem(position) {
|
|
908
|
-
window.requestAnimationFrame(() => {
|
|
909
|
-
const items = menuItems();
|
|
910
|
-
if (!items.length)
|
|
911
|
-
return;
|
|
912
|
-
const index = position === "first"
|
|
913
|
-
? 0
|
|
914
|
-
: position === "last"
|
|
915
|
-
? items.length - 1
|
|
916
|
-
: Math.max(0, Math.min(items.length - 1, position));
|
|
917
|
-
items[index]?.focus();
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
function openMenu() {
|
|
921
|
-
setShouldRenderMenu(true);
|
|
922
|
-
setMenuMotionClass("opening");
|
|
923
|
-
onOpenChange(true);
|
|
924
|
-
}
|
|
925
|
-
function closeMenu() {
|
|
926
|
-
onOpenChange(false);
|
|
927
|
-
}
|
|
928
|
-
function setMenuOpen(nextIsOpen) {
|
|
929
|
-
if (nextIsOpen) {
|
|
930
|
-
openMenu();
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
closeMenu();
|
|
934
|
-
}
|
|
935
|
-
function toggleMenu() {
|
|
936
|
-
const nextIsOpen = !isOpenRef.current;
|
|
937
|
-
setMenuOpen(nextIsOpen);
|
|
938
|
-
return nextIsOpen;
|
|
939
|
-
}
|
|
940
|
-
useEffect(() => {
|
|
941
|
-
if (isOpen) {
|
|
942
|
-
setShouldRenderMenu(true);
|
|
943
|
-
setMenuMotionClass("opening");
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
if (!shouldRenderMenu)
|
|
947
|
-
return;
|
|
948
|
-
setMenuMotionClass("closing");
|
|
949
|
-
const closeTimer = window.setTimeout(() => {
|
|
950
|
-
setShouldRenderMenu(false);
|
|
951
|
-
}, MENU_CLOSE_ANIMATION_MS);
|
|
952
|
-
return () => {
|
|
953
|
-
window.clearTimeout(closeTimer);
|
|
954
|
-
};
|
|
955
|
-
}, [isOpen, shouldRenderMenu]);
|
|
956
|
-
useLayoutEffect(() => {
|
|
957
|
-
if (!shouldRenderMenu) {
|
|
958
|
-
setFixedMenuStyle(undefined);
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
updateFixedMenuStyle();
|
|
962
|
-
const frame = window.requestAnimationFrame(updateFixedMenuStyle);
|
|
963
|
-
window.addEventListener("resize", updateFixedMenuStyle);
|
|
964
|
-
window.addEventListener("scroll", updateFixedMenuStyle, true);
|
|
965
|
-
return () => {
|
|
966
|
-
window.cancelAnimationFrame(frame);
|
|
967
|
-
window.removeEventListener("resize", updateFixedMenuStyle);
|
|
968
|
-
window.removeEventListener("scroll", updateFixedMenuStyle, true);
|
|
969
|
-
};
|
|
970
|
-
}, [shouldRenderMenu, updateFixedMenuStyle]);
|
|
971
|
-
useEffect(() => {
|
|
972
|
-
if (!isOpen)
|
|
973
|
-
return;
|
|
974
|
-
function handlePointerDown(event) {
|
|
975
|
-
const target = event.target;
|
|
976
|
-
if (!menuRef.current?.contains(target) && !menuButtonRef.current?.contains(target)) {
|
|
977
|
-
closeMenu();
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
function handleKeyDown(event) {
|
|
981
|
-
if (event.key === "Escape") {
|
|
982
|
-
closeMenu();
|
|
983
|
-
menuButtonRef.current?.focus();
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
document.addEventListener("pointerdown", handlePointerDown);
|
|
987
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
988
|
-
return () => {
|
|
989
|
-
document.removeEventListener("pointerdown", handlePointerDown);
|
|
990
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
991
|
-
};
|
|
992
|
-
}, [isOpen, onOpenChange]);
|
|
993
|
-
return {
|
|
994
|
-
closeMenu,
|
|
995
|
-
fixedMenuStyle,
|
|
996
|
-
handleMenuButtonKeyDown: (event) => {
|
|
997
|
-
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
998
|
-
event.preventDefault();
|
|
999
|
-
openMenu();
|
|
1000
|
-
focusMenuItem(event.key === "ArrowDown" ? "first" : "last");
|
|
1001
|
-
}
|
|
1002
|
-
},
|
|
1003
|
-
handleMenuKeyDown: (event) => {
|
|
1004
|
-
if (event.key !== "ArrowDown" &&
|
|
1005
|
-
event.key !== "ArrowUp" &&
|
|
1006
|
-
event.key !== "Home" &&
|
|
1007
|
-
event.key !== "End" &&
|
|
1008
|
-
event.key !== "Escape") {
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
event.preventDefault();
|
|
1012
|
-
if (event.key === "Escape") {
|
|
1013
|
-
closeMenu();
|
|
1014
|
-
menuButtonRef.current?.focus();
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
const items = menuItems();
|
|
1018
|
-
if (!items.length)
|
|
1019
|
-
return;
|
|
1020
|
-
const activeIndex = items.findIndex((item) => item === document.activeElement);
|
|
1021
|
-
if (event.key === "Home") {
|
|
1022
|
-
focusMenuItem("first");
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
if (event.key === "End") {
|
|
1026
|
-
focusMenuItem("last");
|
|
1027
|
-
return;
|
|
1028
|
-
}
|
|
1029
|
-
const offset = event.key === "ArrowDown" ? 1 : -1;
|
|
1030
|
-
const nextIndex = activeIndex === -1
|
|
1031
|
-
? (event.key === "ArrowDown" ? 0 : items.length - 1)
|
|
1032
|
-
: (activeIndex + offset + items.length) % items.length;
|
|
1033
|
-
focusMenuItem(nextIndex);
|
|
1034
|
-
},
|
|
1035
|
-
menuButtonRef,
|
|
1036
|
-
menuMotionClass,
|
|
1037
|
-
menuRef,
|
|
1038
|
-
toggleMenu,
|
|
1039
|
-
shouldRenderMenu
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
function FilterMenu({ label, onChange, options, value }) {
|
|
1043
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
1044
|
-
const { closeMenu, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isOpen, setIsOpen);
|
|
1045
|
-
const selectedOption = options.find((option) => option.value === value) ?? options[0];
|
|
1046
|
-
return (<div className="filter-menu" ref={menuRef}>
|
|
1047
|
-
<button aria-expanded={isOpen} aria-haspopup="menu" className="filter-menu-button" onClick={toggleMenu} onKeyDown={handleMenuButtonKeyDown} ref={menuButtonRef} type="button">
|
|
1048
|
-
<span className="filter-menu-label">{label}</span>
|
|
1049
|
-
<span className="filter-menu-value">{selectedOption?.label ?? value}</span>
|
|
1050
|
-
<ChevronDown aria-hidden="true" size={14} strokeWidth={2}/>
|
|
1051
|
-
</button>
|
|
1052
|
-
{shouldRenderMenu ? (<div className={`filter-menu-list menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu">
|
|
1053
|
-
{options.map((option) => {
|
|
1054
|
-
const isSelected = option.value === value;
|
|
1055
|
-
return (<button aria-checked={isSelected} className="filter-menu-item" key={option.value} onClick={() => {
|
|
1056
|
-
onChange(option.value);
|
|
1057
|
-
closeMenu();
|
|
1058
|
-
}} role="menuitemradio" type="button">
|
|
1059
|
-
<span>{option.label}</span>
|
|
1060
|
-
{isSelected ? <Check aria-hidden="true" size={14} strokeWidth={2}/> : null}
|
|
1061
|
-
</button>);
|
|
1062
|
-
})}
|
|
1063
|
-
</div>) : null}
|
|
1064
|
-
</div>);
|
|
1065
|
-
}
|
|
1066
|
-
function FilterToolbar({ filters, snapshot, selectedFilters, onChange }) {
|
|
1067
|
-
if (!filters.length)
|
|
1068
|
-
return null;
|
|
1069
|
-
return (<div className="filter-toolbar" aria-label="Analytics filters">
|
|
1070
|
-
<div className="filter-group">
|
|
1071
|
-
{filters.map((filter) => {
|
|
1072
|
-
const options = Array.from(new Set(getRows(snapshot, filter.dataset).map((row) => String(row[filter.field] ?? ""))))
|
|
1073
|
-
.filter(Boolean)
|
|
1074
|
-
.sort();
|
|
1075
|
-
const filterOptions = [
|
|
1076
|
-
...(filter.includeAll === false ? [] : [{ label: "All", value: "all" }]),
|
|
1077
|
-
...options.map((option) => ({ label: option, value: option }))
|
|
1078
|
-
];
|
|
1079
|
-
const value = selectedFilters[filter.id] ?? filter.defaultValue ?? "all";
|
|
1080
|
-
return (<FilterMenu key={filter.id} label={filter.label} onChange={(nextValue) => onChange({ ...selectedFilters, [filter.id]: nextValue })} options={filterOptions} value={value}/>);
|
|
1081
|
-
})}
|
|
1082
|
-
</div>
|
|
1083
|
-
</div>);
|
|
1084
|
-
}
|
|
1085
|
-
function PanelHeader({ action, children, title, subtitle, titleRowClassName, titleRowProps }) {
|
|
1086
|
-
return (<div className="panel-header">
|
|
1087
|
-
<div {...titleRowProps} className={`panel-title-row ${titleRowClassName ?? ""} ${titleRowProps?.className ?? ""}`.trim()}>
|
|
1088
|
-
{children ?? (<div>
|
|
1089
|
-
<h2>{title}</h2>
|
|
1090
|
-
{subtitle ? <p>{subtitle}</p> : null}
|
|
1091
|
-
</div>)}
|
|
1092
|
-
</div>
|
|
1093
|
-
{action}
|
|
1094
|
-
</div>);
|
|
1095
|
-
}
|
|
1096
|
-
function EditablePageTitle({ ariaLabel, isEditMode, onChange, onRequestEditMode, placeholder, readOnly = false, title }) {
|
|
1097
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
1098
|
-
const textareaRef = useRef(null);
|
|
1099
|
-
const displayTitle = title.trim() ? title : placeholder;
|
|
1100
|
-
useEffect(() => {
|
|
1101
|
-
if (isEditing)
|
|
1102
|
-
resizeEditableTextarea(textareaRef.current);
|
|
1103
|
-
}, [isEditing, title]);
|
|
1104
|
-
function startEditing() {
|
|
1105
|
-
if (readOnly)
|
|
1106
|
-
return;
|
|
1107
|
-
if (!isEditMode)
|
|
1108
|
-
onRequestEditMode();
|
|
1109
|
-
setIsEditing(true);
|
|
1110
|
-
}
|
|
1111
|
-
if (readOnly) {
|
|
1112
|
-
return (<div className="page-title-edit-target page-title-readonly">
|
|
1113
|
-
<h1>{displayTitle}</h1>
|
|
1114
|
-
</div>);
|
|
1115
|
-
}
|
|
1116
|
-
if (isEditing) {
|
|
1117
|
-
return (<textarea aria-label={ariaLabel} autoFocus className="page-title-editor viz-card__no-drag" onBlur={() => setIsEditing(false)} onChange={(event) => {
|
|
1118
|
-
resizeEditableTextarea(event.currentTarget);
|
|
1119
|
-
onChange(event.currentTarget.value);
|
|
1120
|
-
}} placeholder={placeholder} ref={textareaRef} rows={1} value={title}/>);
|
|
1121
|
-
}
|
|
1122
|
-
return (<div aria-label={ariaLabel} className="page-title-edit-target viz-card__no-drag" data-page-title-edit-mode={isEditMode ? "true" : "false"} onClick={() => {
|
|
1123
|
-
if (isEditMode)
|
|
1124
|
-
startEditing();
|
|
1125
|
-
}} onDoubleClick={startEditing} onKeyDown={(event) => {
|
|
1126
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
1127
|
-
event.preventDefault();
|
|
1128
|
-
startEditing();
|
|
1129
|
-
}
|
|
1130
|
-
}} role="button" tabIndex={0}>
|
|
1131
|
-
<h1>{displayTitle}</h1>
|
|
1132
|
-
</div>);
|
|
1133
|
-
}
|
|
1134
|
-
function composeHeaderMarkdown(title, description) {
|
|
1135
|
-
return description?.trim() ? `## ${title}\n\n${description}` : `## ${title}`;
|
|
1136
|
-
}
|
|
1137
|
-
function composeVisualHeaderMarkdown(title, description, headerMarkdown) {
|
|
1138
|
-
if (headerMarkdown?.trim()) {
|
|
1139
|
-
return headerMarkdown;
|
|
1140
|
-
}
|
|
1141
|
-
return composeHeaderMarkdown(title, description);
|
|
1142
|
-
}
|
|
1143
|
-
function composePageTitle(manifest) {
|
|
1144
|
-
const fallbackTitle = manifest?.surface === "report" ? "Data Analytics Report" : "Data Analytics Dashboard";
|
|
1145
|
-
return manifest?.title?.trim() || fallbackTitle;
|
|
1146
|
-
}
|
|
1147
|
-
function composeChartHeaderMarkdown(chart) {
|
|
1148
|
-
return composeVisualHeaderMarkdown(chart.title, chart.showDescription ? chart.subtitle : undefined, chart.headerMarkdown);
|
|
1149
|
-
}
|
|
1150
|
-
function composeTableHeaderMarkdown(table) {
|
|
1151
|
-
return composeVisualHeaderMarkdown(table.title, table.showDescription ? table.subtitle : undefined, table.headerMarkdown);
|
|
1152
|
-
}
|
|
1153
|
-
function reportBlockBodyMarkdown(block) {
|
|
1154
|
-
return typeof block.body === "string" ? block.body : "";
|
|
1155
|
-
}
|
|
1156
|
-
function visibleString(value) {
|
|
1157
|
-
return typeof value === "string" && value.trim() ? value : undefined;
|
|
1158
|
-
}
|
|
1159
|
-
function composeBlockMarkdown(block, textOverride) {
|
|
1160
|
-
if (visibleString(textOverride?.bodyMarkdown))
|
|
1161
|
-
return textOverride.bodyMarkdown;
|
|
1162
|
-
return reportBlockBodyMarkdown(block);
|
|
1163
|
-
}
|
|
1164
|
-
function composeBlockHtml(block, textOverride) {
|
|
1165
|
-
if (visibleString(textOverride?.html))
|
|
1166
|
-
return textOverride.html;
|
|
1167
|
-
return typeof block.body === "string" ? block.body : "";
|
|
1168
|
-
}
|
|
1169
|
-
const REPORT_HTML_BLOCK_CSP = [
|
|
1170
|
-
"default-src 'none'",
|
|
1171
|
-
"base-uri 'none'",
|
|
1172
|
-
"connect-src 'none'",
|
|
1173
|
-
"font-src data:",
|
|
1174
|
-
"form-action 'none'",
|
|
1175
|
-
"frame-src 'none'",
|
|
1176
|
-
"img-src data: blob:",
|
|
1177
|
-
"media-src data: blob:",
|
|
1178
|
-
"object-src 'none'",
|
|
1179
|
-
"script-src 'none'",
|
|
1180
|
-
"style-src 'unsafe-inline'",
|
|
1181
|
-
].join("; ");
|
|
1182
|
-
function sandboxedReportHtml(html) {
|
|
1183
|
-
return `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${REPORT_HTML_BLOCK_CSP}"><meta name="viewport" content="width=device-width, initial-scale=1"><base target="_blank"><style>html,body{margin:0;background:transparent;}img,svg,canvas,video{max-width:100%;height:auto;}</style></head><body>${html}</body></html>`;
|
|
1184
|
-
}
|
|
1185
|
-
function measureHtmlFrameHeight(frame) {
|
|
1186
|
-
const documentElement = frame?.contentDocument?.documentElement;
|
|
1187
|
-
const body = frame?.contentDocument?.body;
|
|
1188
|
-
if (!documentElement && !body)
|
|
1189
|
-
return 0;
|
|
1190
|
-
return Math.ceil(Math.max(documentElement?.scrollHeight ?? 0, body?.scrollHeight ?? 0));
|
|
1191
|
-
}
|
|
1192
|
-
function markdownPlainText(markdown) {
|
|
1193
|
-
return markdown
|
|
1194
|
-
.replace(/^#{1,6}\s+/gm, "")
|
|
1195
|
-
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
1196
|
-
.replace(/`([^`]+)`/g, "$1")
|
|
1197
|
-
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
|
1198
|
-
.trim();
|
|
1199
|
-
}
|
|
1200
|
-
function markdownFirstLine(markdown, fallback) {
|
|
1201
|
-
const first = markdownPlainText(markdown).split(/\r?\n/).find((line) => line.trim());
|
|
1202
|
-
return first?.trim() || fallback;
|
|
1203
|
-
}
|
|
1204
|
-
function ChartBody({ accessIssue, chart, filters, isFullscreen = false, selectedFilters, snapshot }) {
|
|
1205
|
-
const [visibleSeries, setVisibleSeries] = useState();
|
|
1206
|
-
const rawRows = filterRowsForDataset(getRows(snapshot, chart.dataset), chart.dataset, filters, selectedFilters, chartUsedFields(chart));
|
|
1207
|
-
if (accessIssue) {
|
|
1208
|
-
return (<div className="permission-card">
|
|
1209
|
-
<div className="permission-card-title">Missing data access</div>
|
|
1210
|
-
<p>{accessIssue.message}</p>
|
|
1211
|
-
{accessIssue.actionHref ? (<a href={accessIssue.actionHref} rel="noreferrer" target="_blank">
|
|
1212
|
-
{accessIssue.actionLabel ?? "Request access"}
|
|
1213
|
-
</a>) : accessIssue.actionLabel ? (<span>{accessIssue.actionLabel}</span>) : null}
|
|
1214
|
-
</div>);
|
|
1215
|
-
}
|
|
1216
|
-
if (!rawRows.length) {
|
|
1217
|
-
return <div className="empty-state">No rows match the selected filters.</div>;
|
|
1218
|
-
}
|
|
1219
|
-
return (<ChartRenderer chart={chart} height={isFullscreen ? CHART_FULLSCREEN_HEIGHT : undefined} onVisibleSeriesChange={setVisibleSeries} rows={rawRows} surface={isFullscreen ? "explorer" : "card"} visibleSeries={visibleSeries}/>);
|
|
1220
|
-
}
|
|
1221
|
-
function VizCard({ accessIssue, chart, children, isEditMode, isMenuOpen, layout, onCopyResult, onDeleteBlock, onRequestEditMode, onTextChange, onMenuOpenChange, onModalOpen, textOverride }) {
|
|
1222
|
-
const cardRef = useRef(null);
|
|
1223
|
-
const { closeMenu, fixedMenuStyle, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isMenuOpen, onMenuOpenChange);
|
|
1224
|
-
const [imageExportState, setImageExportState] = useState({ status: "idle" });
|
|
1225
|
-
const { getPreparedImageBlob, preparedImageExportStatus, prepareImageExport, resetPreparedImageExport } = usePreparedImageExport(cardRef);
|
|
1226
|
-
const fallbackHeaderMarkdown = textOverride?.title || textOverride?.subtitle
|
|
1227
|
-
? composeHeaderMarkdown(textOverride.title ?? chart.title, textOverride.subtitle ?? chart.subtitle)
|
|
1228
|
-
: composeChartHeaderMarkdown(chart);
|
|
1229
|
-
const displayHeaderMarkdown = textOverride?.headerMarkdown ?? fallbackHeaderMarkdown;
|
|
1230
|
-
const displayChart = {
|
|
1231
|
-
...chart,
|
|
1232
|
-
subtitle: undefined,
|
|
1233
|
-
title: markdownFirstLine(displayHeaderMarkdown, chart.title)
|
|
1234
|
-
};
|
|
1235
|
-
function prepareImageExportQuietly(force = false) {
|
|
1236
|
-
if (accessIssue || !shouldOfferImageClipboardCopy())
|
|
1237
|
-
return;
|
|
1238
|
-
const prepared = prepareImageExport({ force });
|
|
1239
|
-
if (prepared)
|
|
1240
|
-
void prepared.promise.catch(() => undefined);
|
|
1241
|
-
}
|
|
1242
|
-
async function handleCopyAsImage() {
|
|
1243
|
-
if (!cardRef.current)
|
|
1244
|
-
return;
|
|
1245
|
-
setImageExportState({ status: "loading" });
|
|
1246
|
-
try {
|
|
1247
|
-
const copyResult = await copyElementAsImage(cardRef.current, getPreparedImageBlob());
|
|
1248
|
-
setImageExportState({ status: "idle" });
|
|
1249
|
-
resetPreparedImageExport();
|
|
1250
|
-
onCopyResult(imageCopySuccessMessage("Copied widget as image.", copyResult));
|
|
1251
|
-
}
|
|
1252
|
-
catch (error) {
|
|
1253
|
-
setImageExportState({
|
|
1254
|
-
error: error instanceof Error ? error.message : "Failed to copy image.",
|
|
1255
|
-
status: "error"
|
|
1256
|
-
});
|
|
1257
|
-
onCopyResult(error instanceof Error ? error.message : "Failed to copy image.", true);
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
const menuItem = (label, icon, onClick, disabled = false, tone = "default", onPrepare) => (<button className={`viz-card-menu-item ${tone === "danger" ? "viz-card-menu-item-danger" : ""}`.trim()} disabled={disabled} key={label} onFocus={onPrepare} onClick={() => {
|
|
1261
|
-
void onClick();
|
|
1262
|
-
closeMenu();
|
|
1263
|
-
}} onPointerEnter={onPrepare} role="menuitem" type="button">
|
|
1264
|
-
<span aria-hidden="true" className="viz-card-menu-icon">
|
|
1265
|
-
{icon}
|
|
1266
|
-
</span>
|
|
1267
|
-
<span>{label}</span>
|
|
1268
|
-
</button>);
|
|
1269
|
-
return (<section className={`panel chart-panel viz-card ${accessIssue ? "has-permission-issue" : ""}`} id={chart.id} ref={cardRef}>
|
|
1270
|
-
<PanelHeader action={<div className="viz-card-actions" data-image-export-exclude="true" ref={menuRef}>
|
|
1271
|
-
<button aria-expanded={isMenuOpen} aria-label={`Open options for ${displayChart.title}`} className="viz-card-menu-button viz-card__no-drag" onClick={(event) => {
|
|
1272
|
-
event.stopPropagation();
|
|
1273
|
-
const nextIsMenuOpen = toggleMenu();
|
|
1274
|
-
if (nextIsMenuOpen) {
|
|
1275
|
-
prepareImageExportQuietly(true);
|
|
1276
|
-
}
|
|
1277
|
-
else {
|
|
1278
|
-
resetPreparedImageExport();
|
|
1279
|
-
}
|
|
1280
|
-
}} onFocus={() => prepareImageExportQuietly()} onKeyDown={handleMenuButtonKeyDown} onPointerEnter={() => prepareImageExportQuietly()} ref={menuButtonRef} type="button">
|
|
1281
|
-
<Ellipsis aria-hidden="true" size={18} strokeWidth={2}/>
|
|
1282
|
-
</button>
|
|
1283
|
-
{shouldRenderMenu ? (<div className={`viz-card-menu viz-card__no-drag menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu" style={fixedMenuStyle}>
|
|
1284
|
-
{menuItem("Edit chart", <ChartNoAxesCombined size={18} strokeWidth={2}/>, () => onModalOpen({ chart: displayChart, kind: "fullscreen" }))}
|
|
1285
|
-
{menuItem("View data source", <Database size={18} strokeWidth={2}/>, () => onModalOpen({ chart: displayChart, kind: "source" }))}
|
|
1286
|
-
{shouldOfferImageClipboardCopy()
|
|
1287
|
-
? menuItem("Copy as image", <Camera size={18} strokeWidth={2}/>, handleCopyAsImage, Boolean(accessIssue) ||
|
|
1288
|
-
imageExportState.status === "loading" ||
|
|
1289
|
-
preparedImageExportStatus === "pending", "default", prepareImageExportQuietly)
|
|
1290
|
-
: null}
|
|
1291
|
-
{onDeleteBlock ? menuItem("Delete", <Trash2 size={18} strokeWidth={2}/>, onDeleteBlock, false, "danger") : null}
|
|
1292
|
-
</div>) : null}
|
|
1293
|
-
</div>} subtitle={displayChart.subtitle} title={displayChart.title} titleRowClassName="viz-card__drag-handle">
|
|
1294
|
-
<RichMarkdown ariaLabel={`Edit markdown header for ${chart.title}`} className="editable-cell-header" isEditMode={isEditMode} markdown={displayHeaderMarkdown} onMarkdownChange={(nextMarkdown) => onTextChange(chart.id, { headerMarkdown: nextMarkdown })} onRequestEditMode={onRequestEditMode} placeholder={composeChartHeaderMarkdown(chart)} variant="cellHeader"/>
|
|
1295
|
-
</PanelHeader>
|
|
1296
|
-
{children}
|
|
1297
|
-
</section>);
|
|
1298
|
-
}
|
|
1299
|
-
function AccessIssueStrip({ issues }) {
|
|
1300
|
-
if (!issues.length)
|
|
1301
|
-
return null;
|
|
1302
|
-
return (<section className="access-issue-strip" aria-label="Data access issues">
|
|
1303
|
-
<div>
|
|
1304
|
-
<strong>{issues.length === 1 ? "Data access blocker" : "Data access blockers"}</strong>
|
|
1305
|
-
<p>Some report data could not load because the source query could not complete.</p>
|
|
1306
|
-
</div>
|
|
1307
|
-
<ul>
|
|
1308
|
-
{issues.map((issue) => (<li key={issue.id}>
|
|
1309
|
-
<span>{issue.scope ?? issue.dataset ?? issue.sourceId ?? issue.id}</span>
|
|
1310
|
-
<span>{issue.message}</span>
|
|
1311
|
-
{issue.actionHref ? (<a href={issue.actionHref} rel="noreferrer" target="_blank">
|
|
1312
|
-
{issue.actionLabel ?? "Request access"}
|
|
1313
|
-
</a>) : null}
|
|
1314
|
-
</li>))}
|
|
1315
|
-
</ul>
|
|
1316
|
-
</section>);
|
|
1317
|
-
}
|
|
1318
|
-
const SQL_KEYWORDS = new Set([
|
|
1319
|
-
"and",
|
|
1320
|
-
"as",
|
|
1321
|
-
"asc",
|
|
1322
|
-
"by",
|
|
1323
|
-
"case",
|
|
1324
|
-
"desc",
|
|
1325
|
-
"distinct",
|
|
1326
|
-
"else",
|
|
1327
|
-
"end",
|
|
1328
|
-
"from",
|
|
1329
|
-
"group",
|
|
1330
|
-
"in",
|
|
1331
|
-
"is",
|
|
1332
|
-
"join",
|
|
1333
|
-
"left",
|
|
1334
|
-
"like",
|
|
1335
|
-
"max",
|
|
1336
|
-
"min",
|
|
1337
|
-
"not",
|
|
1338
|
-
"null",
|
|
1339
|
-
"on",
|
|
1340
|
-
"or",
|
|
1341
|
-
"order",
|
|
1342
|
-
"over",
|
|
1343
|
-
"partition",
|
|
1344
|
-
"right",
|
|
1345
|
-
"select",
|
|
1346
|
-
"sum",
|
|
1347
|
-
"then",
|
|
1348
|
-
"when",
|
|
1349
|
-
"where",
|
|
1350
|
-
"with"
|
|
1351
|
-
]);
|
|
1352
|
-
const SQL_FUNCTIONS = new Set([
|
|
1353
|
-
"avg",
|
|
1354
|
-
"cast",
|
|
1355
|
-
"coalesce",
|
|
1356
|
-
"concat",
|
|
1357
|
-
"count",
|
|
1358
|
-
"current_date",
|
|
1359
|
-
"date_trunc",
|
|
1360
|
-
"dateadd",
|
|
1361
|
-
"date_add",
|
|
1362
|
-
"round",
|
|
1363
|
-
"nullif"
|
|
1364
|
-
]);
|
|
1365
|
-
const SQL_TOKEN_PATTERN = /(--[^\n]*|'(?:''|[^'])*'|<>|!=|<=|>=|\b\d+(?:\.\d+)?\b|\b[a-zA-Z_][a-zA-Z0-9_]*\b|[(),.;=*+\-/%<>])/g;
|
|
1366
|
-
const SQL_FORMAT_LINE_STARTERS = new Set([
|
|
1367
|
-
"from",
|
|
1368
|
-
"group",
|
|
1369
|
-
"having",
|
|
1370
|
-
"limit",
|
|
1371
|
-
"order",
|
|
1372
|
-
"select",
|
|
1373
|
-
"union",
|
|
1374
|
-
"where",
|
|
1375
|
-
"with"
|
|
1376
|
-
]);
|
|
1377
|
-
const SQL_JOIN_MODIFIERS = new Set([
|
|
1378
|
-
"cross",
|
|
1379
|
-
"full",
|
|
1380
|
-
"inner",
|
|
1381
|
-
"join",
|
|
1382
|
-
"left",
|
|
1383
|
-
"outer",
|
|
1384
|
-
"right"
|
|
1385
|
-
]);
|
|
1386
|
-
function sqlFormatTokens(sql) {
|
|
1387
|
-
const tokens = [];
|
|
1388
|
-
let cursor = 0;
|
|
1389
|
-
for (const match of sql.matchAll(SQL_TOKEN_PATTERN)) {
|
|
1390
|
-
const token = match[0];
|
|
1391
|
-
const index = match.index ?? 0;
|
|
1392
|
-
const rawGap = sql.slice(cursor, index).trim();
|
|
1393
|
-
if (rawGap)
|
|
1394
|
-
tokens.push(...rawGap.split(/\s+/));
|
|
1395
|
-
tokens.push(token);
|
|
1396
|
-
cursor = index + token.length;
|
|
1397
|
-
}
|
|
1398
|
-
const tail = sql.slice(cursor).trim();
|
|
1399
|
-
if (tail)
|
|
1400
|
-
tokens.push(...tail.split(/\s+/));
|
|
1401
|
-
return tokens;
|
|
1402
|
-
}
|
|
1403
|
-
function isSqlWord(token) {
|
|
1404
|
-
return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(token);
|
|
1405
|
-
}
|
|
1406
|
-
function formatSqlForDisplay(sql) {
|
|
1407
|
-
const tokens = sqlFormatTokens(sql.trim());
|
|
1408
|
-
if (!tokens.length)
|
|
1409
|
-
return "";
|
|
1410
|
-
const lines = [];
|
|
1411
|
-
let current = "";
|
|
1412
|
-
let indent = 0;
|
|
1413
|
-
const contextStack = [];
|
|
1414
|
-
let previousToken = "";
|
|
1415
|
-
function currentIndent(level = indent) {
|
|
1416
|
-
return " ".repeat(Math.max(0, level));
|
|
1417
|
-
}
|
|
1418
|
-
function pushLine(nextIndent = indent) {
|
|
1419
|
-
const text = current.trimEnd();
|
|
1420
|
-
if (text.trim())
|
|
1421
|
-
lines.push(text);
|
|
1422
|
-
current = currentIndent(nextIndent);
|
|
1423
|
-
}
|
|
1424
|
-
function append(token, { tight = false } = {}) {
|
|
1425
|
-
if (!current)
|
|
1426
|
-
current = currentIndent();
|
|
1427
|
-
const trimmed = current.trimEnd();
|
|
1428
|
-
const needsSpace = !tight &&
|
|
1429
|
-
trimmed &&
|
|
1430
|
-
!trimmed.endsWith("(") &&
|
|
1431
|
-
!trimmed.endsWith(".") &&
|
|
1432
|
-
token !== "." &&
|
|
1433
|
-
token !== "," &&
|
|
1434
|
-
token !== ")" &&
|
|
1435
|
-
token !== ";";
|
|
1436
|
-
current = `${trimmed}${needsSpace ? " " : ""}${token}`;
|
|
1437
|
-
previousToken = token;
|
|
1438
|
-
}
|
|
1439
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
1440
|
-
const token = tokens[index];
|
|
1441
|
-
const lower = token.toLowerCase();
|
|
1442
|
-
const previousLower = previousToken.toLowerCase();
|
|
1443
|
-
const nextLower = tokens[index + 1]?.toLowerCase?.() ?? "";
|
|
1444
|
-
if (token.startsWith("--")) {
|
|
1445
|
-
if (current.trim())
|
|
1446
|
-
pushLine();
|
|
1447
|
-
append(token);
|
|
1448
|
-
pushLine();
|
|
1449
|
-
continue;
|
|
1450
|
-
}
|
|
1451
|
-
if (SQL_FORMAT_LINE_STARTERS.has(lower)) {
|
|
1452
|
-
if (current.trim() && !current.trimEnd().endsWith("("))
|
|
1453
|
-
pushLine();
|
|
1454
|
-
append(lower.toUpperCase());
|
|
1455
|
-
continue;
|
|
1456
|
-
}
|
|
1457
|
-
if (SQL_JOIN_MODIFIERS.has(lower)) {
|
|
1458
|
-
if (!(lower === "join" && SQL_JOIN_MODIFIERS.has(previousLower)) && current.trim())
|
|
1459
|
-
pushLine();
|
|
1460
|
-
append(lower.toUpperCase());
|
|
1461
|
-
continue;
|
|
1462
|
-
}
|
|
1463
|
-
if (token === "(") {
|
|
1464
|
-
const functionCall = SQL_FUNCTIONS.has(previousLower) ||
|
|
1465
|
-
(isSqlWord(previousToken) && !SQL_KEYWORDS.has(previousLower) && previousLower !== "as");
|
|
1466
|
-
append(token, { tight: functionCall });
|
|
1467
|
-
contextStack.push(functionCall ? "function" : "group");
|
|
1468
|
-
if (!functionCall && (nextLower === "select" || previousLower === "as")) {
|
|
1469
|
-
indent += 1;
|
|
1470
|
-
pushLine();
|
|
1471
|
-
}
|
|
1472
|
-
else if (!functionCall) {
|
|
1473
|
-
indent += 1;
|
|
1474
|
-
}
|
|
1475
|
-
continue;
|
|
1476
|
-
}
|
|
1477
|
-
if (token === ")") {
|
|
1478
|
-
const context = contextStack.pop();
|
|
1479
|
-
const nextIndent = Math.max(0, indent - 1);
|
|
1480
|
-
if (context !== "function" && current.trim() && current.trim() !== currentIndent().trim())
|
|
1481
|
-
pushLine(nextIndent);
|
|
1482
|
-
indent = nextIndent;
|
|
1483
|
-
append(token, { tight: true });
|
|
1484
|
-
continue;
|
|
1485
|
-
}
|
|
1486
|
-
if (token === ",") {
|
|
1487
|
-
append(token, { tight: true });
|
|
1488
|
-
if (contextStack[contextStack.length - 1] !== "function")
|
|
1489
|
-
pushLine();
|
|
1490
|
-
continue;
|
|
1491
|
-
}
|
|
1492
|
-
if (token === ".") {
|
|
1493
|
-
append(token, { tight: true });
|
|
1494
|
-
continue;
|
|
1495
|
-
}
|
|
1496
|
-
if (token === ";") {
|
|
1497
|
-
append(token, { tight: true });
|
|
1498
|
-
pushLine();
|
|
1499
|
-
continue;
|
|
1500
|
-
}
|
|
1501
|
-
append(SQL_KEYWORDS.has(lower) || SQL_FUNCTIONS.has(lower) ? lower.toUpperCase() : token);
|
|
1502
|
-
}
|
|
1503
|
-
if (current.trim())
|
|
1504
|
-
pushLine();
|
|
1505
|
-
return lines.join("\n");
|
|
1506
|
-
}
|
|
1507
|
-
function highlightSql(sql) {
|
|
1508
|
-
const nodes = [];
|
|
1509
|
-
let cursor = 0;
|
|
1510
|
-
for (const match of sql.matchAll(SQL_TOKEN_PATTERN)) {
|
|
1511
|
-
const token = match[0];
|
|
1512
|
-
const index = match.index ?? 0;
|
|
1513
|
-
if (index > cursor) {
|
|
1514
|
-
nodes.push(sql.slice(cursor, index));
|
|
1515
|
-
}
|
|
1516
|
-
const lower = token.toLowerCase();
|
|
1517
|
-
let className = "sql-token";
|
|
1518
|
-
if (token.startsWith("--")) {
|
|
1519
|
-
className += " comment";
|
|
1520
|
-
}
|
|
1521
|
-
else if (token.startsWith("'")) {
|
|
1522
|
-
className += " string";
|
|
1523
|
-
}
|
|
1524
|
-
else if (/^\d/.test(token)) {
|
|
1525
|
-
className += " number";
|
|
1526
|
-
}
|
|
1527
|
-
else if (SQL_KEYWORDS.has(lower)) {
|
|
1528
|
-
className += " keyword";
|
|
1529
|
-
}
|
|
1530
|
-
else if (SQL_FUNCTIONS.has(lower)) {
|
|
1531
|
-
className += " function";
|
|
1532
|
-
}
|
|
1533
|
-
else if (/^[(),.;=*+\-/%]$/.test(token)) {
|
|
1534
|
-
className += " punctuation";
|
|
1535
|
-
}
|
|
1536
|
-
else {
|
|
1537
|
-
className += " identifier";
|
|
1538
|
-
}
|
|
1539
|
-
nodes.push(<span className={className} key={`${index}-${token}`}>
|
|
1540
|
-
{token}
|
|
1541
|
-
</span>);
|
|
1542
|
-
cursor = index + token.length;
|
|
1543
|
-
}
|
|
1544
|
-
if (cursor < sql.length) {
|
|
1545
|
-
nodes.push(sql.slice(cursor));
|
|
1546
|
-
}
|
|
1547
|
-
return nodes;
|
|
1548
|
-
}
|
|
1549
|
-
const SOURCE_FETCH_TIMEOUT_MS = 10000;
|
|
1550
|
-
function DataSourceDetails({ children, details, source, sourceQuery }) {
|
|
1551
|
-
return (<div className="source-modal-sections">
|
|
1552
|
-
<section className="source-modal-section">
|
|
1553
|
-
<h3>Details</h3>
|
|
1554
|
-
<dl className="details-grid source-details-grid">
|
|
1555
|
-
<div>
|
|
1556
|
-
<dt>Dataset</dt>
|
|
1557
|
-
<dd>{details.dataset}</dd>
|
|
1558
|
-
</div>
|
|
1559
|
-
<div>
|
|
1560
|
-
<dt>Tables used</dt>
|
|
1561
|
-
<dd>{detailList(details.tables)}</dd>
|
|
1562
|
-
</div>
|
|
1563
|
-
<div>
|
|
1564
|
-
<dt>Filters</dt>
|
|
1565
|
-
<dd>{detailText(details.filters)}</dd>
|
|
1566
|
-
</div>
|
|
1567
|
-
<div>
|
|
1568
|
-
<dt>Metric definitions</dt>
|
|
1569
|
-
<dd>{detailList(details.metricDefinitions)}</dd>
|
|
1570
|
-
</div>
|
|
1571
|
-
<div>
|
|
1572
|
-
<dt>Snapshot</dt>
|
|
1573
|
-
<dd>{formatDate(details.snapshot)}</dd>
|
|
1574
|
-
</div>
|
|
1575
|
-
</dl>
|
|
1576
|
-
</section>
|
|
1577
|
-
<section className="source-modal-section">
|
|
1578
|
-
<h3>Source query</h3>
|
|
1579
|
-
<SourceQueryBlock source={source} sourceQuery={sourceQuery}/>
|
|
1580
|
-
</section>
|
|
1581
|
-
<section className="source-modal-section">
|
|
1582
|
-
<h3>Data table</h3>
|
|
1583
|
-
{children}
|
|
1584
|
-
</section>
|
|
1585
|
-
</div>);
|
|
1586
|
-
}
|
|
1587
|
-
function SourceQueryBlock({ source, sourceQuery }) {
|
|
1588
|
-
const [sourceContent, setSourceContent] = useState({ status: "idle" });
|
|
1589
|
-
const [copyStatus, setCopyStatus] = useState("idle");
|
|
1590
|
-
const codeRef = useRef(null);
|
|
1591
|
-
const inlineSourceText = queryTextFromSourceQuery(sourceQueryFromSourceSpec(source)) || queryTextFromSourceQuery(sourceQuery) || "";
|
|
1592
|
-
const displaySourceText = sourceContent.status === "loaded" ? formatSqlForDisplay(sourceContent.text) : "";
|
|
1593
|
-
useEffect(() => {
|
|
1594
|
-
if (inlineSourceText) {
|
|
1595
|
-
setSourceContent({ status: "loaded", text: inlineSourceText });
|
|
1596
|
-
return;
|
|
1597
|
-
}
|
|
1598
|
-
if (!source?.path) {
|
|
1599
|
-
setSourceContent({ status: "idle" });
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
let cancelled = false;
|
|
1603
|
-
const controller = new AbortController();
|
|
1604
|
-
const timeout = window.setTimeout(() => controller.abort(), SOURCE_FETCH_TIMEOUT_MS);
|
|
1605
|
-
setSourceContent({ status: "loading" });
|
|
1606
|
-
fetch(`/api/source-file?path=${encodeURIComponent(source.path)}`, {
|
|
1607
|
-
cache: "no-store",
|
|
1608
|
-
signal: controller.signal
|
|
1609
|
-
})
|
|
1610
|
-
.then(async (response) => {
|
|
1611
|
-
const text = await response.text();
|
|
1612
|
-
if (!response.ok)
|
|
1613
|
-
throw new Error(text || `Request failed: ${response.status}`);
|
|
1614
|
-
return text;
|
|
1615
|
-
})
|
|
1616
|
-
.then((text) => {
|
|
1617
|
-
if (!cancelled)
|
|
1618
|
-
setSourceContent({ status: "loaded", text });
|
|
1619
|
-
})
|
|
1620
|
-
.catch((error) => {
|
|
1621
|
-
if (!cancelled) {
|
|
1622
|
-
const isAbort = error instanceof Error && error.name === "AbortError";
|
|
1623
|
-
setSourceContent({
|
|
1624
|
-
error: isAbort
|
|
1625
|
-
? "Source request timed out. Refresh the app and try again."
|
|
1626
|
-
: error instanceof Error
|
|
1627
|
-
? error.message
|
|
1628
|
-
: "Failed to load source",
|
|
1629
|
-
status: "error"
|
|
1630
|
-
});
|
|
1631
|
-
}
|
|
1632
|
-
})
|
|
1633
|
-
.finally(() => {
|
|
1634
|
-
window.clearTimeout(timeout);
|
|
1635
|
-
});
|
|
1636
|
-
return () => {
|
|
1637
|
-
cancelled = true;
|
|
1638
|
-
window.clearTimeout(timeout);
|
|
1639
|
-
controller.abort();
|
|
1640
|
-
};
|
|
1641
|
-
}, [inlineSourceText, source?.path]);
|
|
1642
|
-
useEffect(() => {
|
|
1643
|
-
if (copyStatus === "idle")
|
|
1644
|
-
return;
|
|
1645
|
-
const timeout = window.setTimeout(() => setCopyStatus("idle"), 1800);
|
|
1646
|
-
return () => window.clearTimeout(timeout);
|
|
1647
|
-
}, [copyStatus]);
|
|
1648
|
-
async function handleCopyQuery(text) {
|
|
1649
|
-
try {
|
|
1650
|
-
await copyTextToClipboard(text);
|
|
1651
|
-
setCopyStatus("copied");
|
|
1652
|
-
}
|
|
1653
|
-
catch {
|
|
1654
|
-
setCopyStatus("blocked");
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
if (sourceContent.status === "loading")
|
|
1658
|
-
return <>Loading source...</>;
|
|
1659
|
-
if (sourceContent.status === "error") {
|
|
1660
|
-
return <span className="source-error">{sourceContent.error}</span>;
|
|
1661
|
-
}
|
|
1662
|
-
if (sourceContent.status === "loaded") {
|
|
1663
|
-
return (<div className="source-query-shell">
|
|
1664
|
-
{source?.path || source?.href || source?.label || sourceQuery?.id || sourceQuery?.engine ? (<div className="source-query-meta">
|
|
1665
|
-
<span>
|
|
1666
|
-
{source?.href ? (<a href={source.href} rel="noreferrer" target="_blank">
|
|
1667
|
-
{source.path ?? source.label}
|
|
1668
|
-
</a>) : (source?.path ?? source?.label ?? sourceQuery?.id ?? sourceQuery?.engine)}
|
|
1669
|
-
</span>
|
|
1670
|
-
<button className="source-query-copy" onClick={() => void handleCopyQuery(displaySourceText || sourceContent.text)} type="button">
|
|
1671
|
-
<Copy aria-hidden="true" size={14} strokeWidth={2}/>
|
|
1672
|
-
{copyStatus === "copied"
|
|
1673
|
-
? "Copied"
|
|
1674
|
-
: copyStatus === "blocked"
|
|
1675
|
-
? "Copy failed"
|
|
1676
|
-
: "Copy query"}
|
|
1677
|
-
</button>
|
|
1678
|
-
</div>) : null}
|
|
1679
|
-
<pre className="source-query">
|
|
1680
|
-
<code ref={codeRef}>{highlightSql(displaySourceText || sourceContent.text)}</code>
|
|
1681
|
-
</pre>
|
|
1682
|
-
</div>);
|
|
1683
|
-
}
|
|
1684
|
-
return <>No source query file mapped.</>;
|
|
1685
|
-
}
|
|
1686
|
-
function useModalScrollLock(enabled) {
|
|
1687
|
-
useEffect(() => {
|
|
1688
|
-
if (!enabled)
|
|
1689
|
-
return undefined;
|
|
1690
|
-
const previousOverflow = document.body.style.overflow;
|
|
1691
|
-
const previousOverscrollBehavior = document.body.style.overscrollBehavior;
|
|
1692
|
-
document.body.style.overflow = "hidden";
|
|
1693
|
-
document.body.style.overscrollBehavior = "none";
|
|
1694
|
-
return () => {
|
|
1695
|
-
document.body.style.overflow = previousOverflow;
|
|
1696
|
-
document.body.style.overscrollBehavior = previousOverscrollBehavior;
|
|
1697
|
-
};
|
|
1698
|
-
}, [enabled]);
|
|
1699
|
-
}
|
|
1700
|
-
function sourcePreviewColumns(rows, columns = []) {
|
|
1701
|
-
if (columns.length)
|
|
1702
|
-
return columns;
|
|
1703
|
-
const fields = [];
|
|
1704
|
-
const seen = new Set();
|
|
1705
|
-
for (const row of rows) {
|
|
1706
|
-
for (const field of Object.keys(row)) {
|
|
1707
|
-
if (!seen.has(field)) {
|
|
1708
|
-
seen.add(field);
|
|
1709
|
-
fields.push(field);
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
return fields.map((field) => ({ field, label: field }));
|
|
1714
|
-
}
|
|
1715
|
-
function SourceDataTable({ columns = [], dataset, density = "dense", rows = [] }) {
|
|
1716
|
-
const previewRows = useMemo(() => asArray(rows).filter((row) => row && typeof row === "object"), [rows]);
|
|
1717
|
-
const previewDataset = dataset ?? "__source_preview_rows";
|
|
1718
|
-
const table = useMemo(() => ({
|
|
1719
|
-
columns: sourcePreviewColumns(previewRows, columns),
|
|
1720
|
-
dataset: previewDataset,
|
|
1721
|
-
id: `source-preview-${previewDataset}`
|
|
1722
|
-
}), [columns, previewDataset, previewRows]);
|
|
1723
|
-
const snapshot = useMemo(() => ({ datasets: { [previewDataset]: previewRows } }), [previewDataset, previewRows]);
|
|
1724
|
-
const [columnWidths, setColumnWidths] = useState({});
|
|
1725
|
-
useEffect(() => {
|
|
1726
|
-
setColumnWidths({});
|
|
1727
|
-
}, [table.id]);
|
|
1728
|
-
return (<div className="source-data-table">
|
|
1729
|
-
<TableContent allowColumnResize={false} columnWidths={columnWidths} density={density} filters={EMPTY_FILTERS} onColumnWidthsChange={(_tableId, nextWidths) => setColumnWidths(nextWidths)} selectedFilters={{}} snapshot={snapshot} table={table}/>
|
|
1730
|
-
</div>);
|
|
1731
|
-
}
|
|
1732
|
-
function querySourceForChart(chart, sources) {
|
|
1733
|
-
const directSource = sourceForChart(chart, sources);
|
|
1734
|
-
return (directSource ??
|
|
1735
|
-
sources.find((source) => {
|
|
1736
|
-
const label = source.label?.toLowerCase() ?? "";
|
|
1737
|
-
const path = source.path?.toLowerCase() ?? "";
|
|
1738
|
-
return path.endsWith(".sql") || label.includes("sql") || label.includes("quer");
|
|
1739
|
-
}) ??
|
|
1740
|
-
null);
|
|
1741
|
-
}
|
|
1742
|
-
function chartMetricsForBuildDetails(chart) {
|
|
1743
|
-
const metrics = [];
|
|
1744
|
-
const seen = new Set();
|
|
1745
|
-
function addMetric(field, label) {
|
|
1746
|
-
if (!field || seen.has(field))
|
|
1747
|
-
return;
|
|
1748
|
-
seen.add(field);
|
|
1749
|
-
metrics.push({ field, label: label || field });
|
|
1750
|
-
}
|
|
1751
|
-
addMetric(chart.encodings?.y?.field, chart.encodings?.y?.label);
|
|
1752
|
-
for (const field of chart.encodings?.y?.fields ?? []) {
|
|
1753
|
-
addMetric(field, field);
|
|
1754
|
-
}
|
|
1755
|
-
return metrics;
|
|
1756
|
-
}
|
|
1757
|
-
function ChartSourceModalDialog({ activeFilters, chart, manifest, onClose, rows = [], snapshot }) {
|
|
1758
|
-
const dialogRef = useRef(null);
|
|
1759
|
-
const source = querySourceForChart(chart, manifest?.sources ?? []);
|
|
1760
|
-
const sourceQuery = sourceQueryFromSourceSpec(source);
|
|
1761
|
-
const buildDetails = sourceBuildDetails({
|
|
1762
|
-
activeFilters,
|
|
1763
|
-
dataset: chart.dataset,
|
|
1764
|
-
metrics: chartMetricsForBuildDetails(chart),
|
|
1765
|
-
source,
|
|
1766
|
-
sourceQuery,
|
|
1767
|
-
snapshot
|
|
1768
|
-
});
|
|
1769
|
-
useModalScrollLock(true);
|
|
1770
|
-
useEffect(() => {
|
|
1771
|
-
const dialog = dialogRef.current;
|
|
1772
|
-
if (dialog && !dialog.open) {
|
|
1773
|
-
dialog.showModal();
|
|
1774
|
-
}
|
|
1775
|
-
}, []);
|
|
1776
|
-
return (<dialog aria-labelledby="chart-source-modal-title" className="native-modal source-modal" onCancel={onClose} onClick={(event) => {
|
|
1777
|
-
if (event.target === event.currentTarget) {
|
|
1778
|
-
event.currentTarget.close();
|
|
1779
|
-
}
|
|
1780
|
-
}} onClose={onClose} ref={dialogRef}>
|
|
1781
|
-
<section className="modal-panel source-modal-panel">
|
|
1782
|
-
<div className="modal-header">
|
|
1783
|
-
<div>
|
|
1784
|
-
<h2 id="chart-source-modal-title">Data source</h2>
|
|
1785
|
-
<p>{chart.title}</p>
|
|
1786
|
-
</div>
|
|
1787
|
-
<button aria-label="Close data source" className="modal-close-button" onClick={() => dialogRef.current?.close()} type="button">
|
|
1788
|
-
<X aria-hidden="true" size={20} strokeWidth={2}/>
|
|
1789
|
-
</button>
|
|
1790
|
-
</div>
|
|
1791
|
-
<DataSourceDetails details={buildDetails} source={source} sourceQuery={sourceQuery}>
|
|
1792
|
-
<SourceDataTable dataset={chart.dataset} rows={rows}/>
|
|
1793
|
-
</DataSourceDetails>
|
|
1794
|
-
</section>
|
|
1795
|
-
</dialog>);
|
|
1796
|
-
}
|
|
1797
|
-
function safeScriptJson(value) {
|
|
1798
|
-
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1799
|
-
}
|
|
1800
|
-
function chartWidgetDetailHtml(html, widgetInstanceId) {
|
|
1801
|
-
const bridge = `<script>
|
|
1802
|
-
window.openai = {
|
|
1803
|
-
...(window.openai || {}),
|
|
1804
|
-
availableDisplayModes: ["modal"],
|
|
1805
|
-
displayMode: "modal",
|
|
1806
|
-
widgetInstanceId: ${safeScriptJson(widgetInstanceId)},
|
|
1807
|
-
requestDisplayMode(request) {
|
|
1808
|
-
const mode = typeof request === "string" ? request : request && request.mode;
|
|
1809
|
-
window.parent.postMessage({ type: "datascience-chart-widget-display-mode", mode }, "*");
|
|
1810
|
-
return Promise.resolve({ mode });
|
|
1811
|
-
},
|
|
1812
|
-
sendFollowUpMessage(payload) {
|
|
1813
|
-
window.parent.postMessage(
|
|
1814
|
-
{
|
|
1815
|
-
originUrl: payload && payload.originUrl,
|
|
1816
|
-
prompt: payload && payload.prompt,
|
|
1817
|
-
type: "datascience-chart-widget-codex-prompt"
|
|
1818
|
-
},
|
|
1819
|
-
"*"
|
|
1820
|
-
);
|
|
1821
|
-
return Promise.resolve({ ok: true });
|
|
1822
|
-
},
|
|
1823
|
-
openCodexPrompt(payload) {
|
|
1824
|
-
return this.sendFollowUpMessage(payload);
|
|
1825
|
-
}
|
|
1826
|
-
};
|
|
1827
|
-
</script>`;
|
|
1828
|
-
return html.includes("</head>") ? html.replace("</head>", `${bridge}</head>`) : `${bridge}${html}`;
|
|
1829
|
-
}
|
|
1830
|
-
function shouldUseHostedChartModal(packageInfo) {
|
|
1831
|
-
return packageInfo?.hostedReadOnly === true || packageInfo?.deliveryMode === "site_creator";
|
|
1832
|
-
}
|
|
1833
|
-
function ChartDetailPage({ accessIssue, chart, filters, manifest, onChartSpecChange, onClose, packageInfo, rows, selectedFilters, snapshot }) {
|
|
1834
|
-
const dialogRef = useRef(null);
|
|
1835
|
-
const iframeRef = useRef(null);
|
|
1836
|
-
const pendingPostTimersRef = useRef([]);
|
|
1837
|
-
const [widgetHtml, setWidgetHtml] = useState(null);
|
|
1838
|
-
const [widgetError, setWidgetError] = useState(null);
|
|
1839
|
-
const [sourceQueryText, setSourceQueryText] = useState(null);
|
|
1840
|
-
const source = querySourceForChart(chart, manifest?.sources ?? []);
|
|
1841
|
-
const hostedChartModal = shouldUseHostedChartModal(packageInfo);
|
|
1842
|
-
const widgetInstanceId = `report-chart-detail-${chart.id}`;
|
|
1843
|
-
const widgetPayload = useMemo(() => chartWidgetPayload(chart, rows, source, snapshot, sourceQueryText), [chart, rows, source, snapshot, sourceQueryText]);
|
|
1844
|
-
const widgetUrl = useMemo(() => `/api/inline-chart-widget?displayMode=modal&widgetInstanceId=${encodeURIComponent(widgetInstanceId)}`, [widgetInstanceId]);
|
|
1845
|
-
const clearPendingPostTimers = useCallback(() => {
|
|
1846
|
-
pendingPostTimersRef.current.forEach((timer) => window.clearTimeout(timer));
|
|
1847
|
-
pendingPostTimersRef.current = [];
|
|
1848
|
-
}, []);
|
|
1849
|
-
const postWidgetPayload = useCallback(() => {
|
|
1850
|
-
iframeRef.current?.contentWindow?.postMessage({
|
|
1851
|
-
displayMode: "modal",
|
|
1852
|
-
payload: widgetPayload,
|
|
1853
|
-
targetWidgetInstanceId: widgetInstanceId
|
|
1854
|
-
}, "*");
|
|
1855
|
-
}, [widgetInstanceId, widgetPayload]);
|
|
1856
|
-
const scheduleWidgetPayloadPosts = useCallback(() => {
|
|
1857
|
-
clearPendingPostTimers();
|
|
1858
|
-
pendingPostTimersRef.current = [0, 50, 150, 350].map((delay) => window.setTimeout(postWidgetPayload, delay));
|
|
1859
|
-
}, [clearPendingPostTimers, postWidgetPayload]);
|
|
1860
|
-
useEffect(() => {
|
|
1861
|
-
const dialog = dialogRef.current;
|
|
1862
|
-
if (dialog && !dialog.open) {
|
|
1863
|
-
dialog.showModal();
|
|
1864
|
-
}
|
|
1865
|
-
}, []);
|
|
1866
|
-
useEffect(() => {
|
|
1867
|
-
let cancelled = false;
|
|
1868
|
-
if (hostedChartModal) {
|
|
1869
|
-
setWidgetHtml(null);
|
|
1870
|
-
setWidgetError(null);
|
|
1871
|
-
return () => {
|
|
1872
|
-
cancelled = true;
|
|
1873
|
-
};
|
|
1874
|
-
}
|
|
1875
|
-
setWidgetHtml(null);
|
|
1876
|
-
setWidgetError(null);
|
|
1877
|
-
void fetch(widgetUrl)
|
|
1878
|
-
.then((response) => {
|
|
1879
|
-
if (!response.ok)
|
|
1880
|
-
throw new Error(`Shared chart detail failed to load (${response.status}).`);
|
|
1881
|
-
return response.text();
|
|
1882
|
-
})
|
|
1883
|
-
.then((html) => {
|
|
1884
|
-
if (!cancelled)
|
|
1885
|
-
setWidgetHtml(chartWidgetDetailHtml(html, widgetInstanceId));
|
|
1886
|
-
})
|
|
1887
|
-
.catch((error) => {
|
|
1888
|
-
if (!cancelled)
|
|
1889
|
-
setWidgetError(error instanceof Error ? error.message : "Shared chart detail failed to load.");
|
|
1890
|
-
});
|
|
1891
|
-
return () => {
|
|
1892
|
-
cancelled = true;
|
|
1893
|
-
};
|
|
1894
|
-
}, [hostedChartModal, widgetInstanceId, widgetUrl]);
|
|
1895
|
-
useEffect(() => {
|
|
1896
|
-
let cancelled = false;
|
|
1897
|
-
if (hostedChartModal) {
|
|
1898
|
-
setSourceQueryText(null);
|
|
1899
|
-
return () => {
|
|
1900
|
-
cancelled = true;
|
|
1901
|
-
};
|
|
1902
|
-
}
|
|
1903
|
-
const inlineSourceText = queryTextFromSourceQuery(sourceQueryFromSourceSpec(source));
|
|
1904
|
-
if (inlineSourceText) {
|
|
1905
|
-
setSourceQueryText(inlineSourceText);
|
|
1906
|
-
return () => {
|
|
1907
|
-
cancelled = true;
|
|
1908
|
-
};
|
|
1909
|
-
}
|
|
1910
|
-
if (!source?.path) {
|
|
1911
|
-
setSourceQueryText(null);
|
|
1912
|
-
return () => {
|
|
1913
|
-
cancelled = true;
|
|
1914
|
-
};
|
|
1915
|
-
}
|
|
1916
|
-
setSourceQueryText(null);
|
|
1917
|
-
void fetch(`/api/source-file?path=${encodeURIComponent(source.path)}`, {
|
|
1918
|
-
headers: { Accept: "text/plain" }
|
|
1919
|
-
})
|
|
1920
|
-
.then((response) => {
|
|
1921
|
-
if (!response.ok)
|
|
1922
|
-
return "";
|
|
1923
|
-
return response.text();
|
|
1924
|
-
})
|
|
1925
|
-
.then((text) => {
|
|
1926
|
-
if (!cancelled)
|
|
1927
|
-
setSourceQueryText(text.trim() || null);
|
|
1928
|
-
})
|
|
1929
|
-
.catch(() => {
|
|
1930
|
-
if (!cancelled)
|
|
1931
|
-
setSourceQueryText(null);
|
|
1932
|
-
});
|
|
1933
|
-
return () => {
|
|
1934
|
-
cancelled = true;
|
|
1935
|
-
};
|
|
1936
|
-
}, [hostedChartModal, source?.path, source?.query?.query]);
|
|
1937
|
-
useEffect(() => {
|
|
1938
|
-
if (hostedChartModal || !widgetHtml)
|
|
1939
|
-
return;
|
|
1940
|
-
scheduleWidgetPayloadPosts();
|
|
1941
|
-
}, [hostedChartModal, scheduleWidgetPayloadPosts, widgetHtml]);
|
|
1942
|
-
useEffect(() => clearPendingPostTimers, [clearPendingPostTimers]);
|
|
1943
|
-
useEffect(() => {
|
|
1944
|
-
if (hostedChartModal)
|
|
1945
|
-
return undefined;
|
|
1946
|
-
function handleWidgetMessage(event) {
|
|
1947
|
-
const data = event.data;
|
|
1948
|
-
if (!data || typeof data !== "object")
|
|
1949
|
-
return;
|
|
1950
|
-
if (data.type === "datascience-chart-widget-display-mode" && data.mode === "inline") {
|
|
1951
|
-
onClose();
|
|
1952
|
-
}
|
|
1953
|
-
if (data.type === "datascience-chart-widget-spec-reset" && data.widgetInstanceId === widgetInstanceId) {
|
|
1954
|
-
onChartSpecChange(chart.id, null);
|
|
1955
|
-
}
|
|
1956
|
-
if (data.type === "datascience-chart-widget-spec-change" && data.widgetInstanceId === widgetInstanceId) {
|
|
1957
|
-
onChartSpecChange(chart.id, data.visualization_spec);
|
|
1958
|
-
}
|
|
1959
|
-
if (data.type === "datascience-chart-widget-codex-prompt" && typeof data.prompt === "string") {
|
|
1960
|
-
sendCodexPromptToHost(data.prompt, null);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
window.addEventListener("message", handleWidgetMessage);
|
|
1964
|
-
return () => window.removeEventListener("message", handleWidgetMessage);
|
|
1965
|
-
}, [chart.id, hostedChartModal, onChartSpecChange, onClose, widgetInstanceId]);
|
|
1966
|
-
return (<dialog aria-label={`Edit ${chart.title}`} className={`native-modal chart-explore-modal ${hostedChartModal ? "chart-hosted-explore-modal" : ""}`.trim()} onCancel={onClose} onClick={(event) => {
|
|
1967
|
-
if (event.target === event.currentTarget) {
|
|
1968
|
-
event.currentTarget.close();
|
|
1969
|
-
}
|
|
1970
|
-
}} onClose={onClose} ref={dialogRef}>
|
|
1971
|
-
<section className={`modal-panel chart-explore-panel ${hostedChartModal ? "chart-hosted-explore-panel" : ""}`.trim()}>
|
|
1972
|
-
<section aria-label={`Edit ${chart.title}`} className={`chart-detail-page unified-chart-detail-page chart-explore-body ${hostedChartModal ? "chart-hosted-explore-body" : ""}`.trim()}>
|
|
1973
|
-
{hostedChartModal ? (<ChartBody accessIssue={accessIssue} chart={chart} filters={filters} isFullscreen selectedFilters={selectedFilters} snapshot={snapshot}/>) : null}
|
|
1974
|
-
{!hostedChartModal && !widgetHtml && !widgetError ? <div className="chart-detail-loading">Loading chart detail...</div> : null}
|
|
1975
|
-
{widgetError ? <div className="empty-state error-state">{widgetError}</div> : null}
|
|
1976
|
-
{!hostedChartModal ? (<iframe className="unified-chart-detail-frame" hidden={!widgetHtml || Boolean(widgetError)} onLoad={scheduleWidgetPayloadPosts} ref={iframeRef} srcDoc={widgetHtml ?? ""} title={`Edit ${chart.title}`}/>) : null}
|
|
1977
|
-
</section>
|
|
1978
|
-
</section>
|
|
1979
|
-
</dialog>);
|
|
1980
|
-
}
|
|
1981
|
-
function chartWidgetColumnType(column, value) {
|
|
1982
|
-
if (column?.type === "date")
|
|
1983
|
-
return "date";
|
|
1984
|
-
if (column?.format || column?.type === "currency" || column?.type === "number" || column?.type === "percent")
|
|
1985
|
-
return "number";
|
|
1986
|
-
if (typeof value === "number")
|
|
1987
|
-
return "number";
|
|
1988
|
-
if (typeof value === "string" && /^\d{4}-\d{2}(?:-\d{2})?(?:$|[T\s])/.test(value))
|
|
1989
|
-
return "date";
|
|
1990
|
-
return "text";
|
|
1991
|
-
}
|
|
1992
|
-
function chartWidgetEncodingColumn(chart, role, rows) {
|
|
1993
|
-
const field = chartEncodingField(chart, role);
|
|
1994
|
-
if (!field)
|
|
1995
|
-
return null;
|
|
1996
|
-
const encoding = chartEncoding(chart, role);
|
|
1997
|
-
return {
|
|
1998
|
-
key: field,
|
|
1999
|
-
label: chartEncodingLabel(chart, role, field),
|
|
2000
|
-
type: encoding.type === "temporal" ? "date" : encoding.type === "quantitative" ? "number" : chartWidgetColumnType(encoding, rows[0]?.[field]),
|
|
2001
|
-
unit: encoding.unit
|
|
2002
|
-
};
|
|
2003
|
-
}
|
|
2004
|
-
function chartWidgetSource(source, sourceQuery, queryText, snapshot) {
|
|
2005
|
-
return {
|
|
2006
|
-
id: source?.id,
|
|
2007
|
-
label: source?.label,
|
|
2008
|
-
path: source?.path,
|
|
2009
|
-
href: source?.href,
|
|
2010
|
-
query: {
|
|
2011
|
-
engine: sourceQuery?.engine ?? source?.label ?? "report manifest",
|
|
2012
|
-
executed_at: sourceQuery?.executed_at ?? snapshot?.generatedAt,
|
|
2013
|
-
id: sourceQuery?.id ?? source?.path ?? source?.href,
|
|
2014
|
-
url: sourceQuery?.url,
|
|
2015
|
-
sql: queryText || "",
|
|
2016
|
-
description: sourceQuery?.description,
|
|
2017
|
-
language: sourceQuery?.language ?? (source?.path?.toLowerCase().endsWith(".sql") ? "SQL" : undefined),
|
|
2018
|
-
tables_used: sourceQuery?.tables_used,
|
|
2019
|
-
filters: sourceQuery?.filters,
|
|
2020
|
-
metric_definitions: sourceQuery?.metric_definitions
|
|
2021
|
-
}
|
|
2022
|
-
};
|
|
2023
|
-
}
|
|
2024
|
-
function chartWidgetPayloadFromEncodings(chart, rows, source, snapshot, sourceQueryText) {
|
|
2025
|
-
const xField = chartEncodingField(chart, "x");
|
|
2026
|
-
const yField = chartEncodingField(chart, "y");
|
|
2027
|
-
const yFields = chartEncodingFields(chart, "y");
|
|
2028
|
-
const colorField = chartEncodingField(chart, "color");
|
|
2029
|
-
const chartSourceQuery = sourceQueryFromChartSpec(chart);
|
|
2030
|
-
const sourceSpecQuery = sourceQueryFromSourceSpec(source);
|
|
2031
|
-
const sourceQuery = chartSourceQuery ?? sourceSpecQuery;
|
|
2032
|
-
const queryText = queryTextFromSourceQuery(chartSourceQuery) ||
|
|
2033
|
-
queryTextFromSourceQuery(sourceSpecQuery) ||
|
|
2034
|
-
sourceQueryText?.trim();
|
|
2035
|
-
const unit = chart.unit ?? chartEncoding(chart, "y").unit ?? (chart.valueFormat === "currency" ? "USD" : chart.valueFormat === "percent" ? "%" : undefined);
|
|
2036
|
-
const settings = chartWidgetSettings(chart);
|
|
2037
|
-
if (xField && yField) {
|
|
2038
|
-
const columns = [
|
|
2039
|
-
chartWidgetEncodingColumn(chart, "x", rows),
|
|
2040
|
-
colorField ? chartWidgetEncodingColumn(chart, "color", rows) : null,
|
|
2041
|
-
chartWidgetEncodingColumn(chart, "y", rows)
|
|
2042
|
-
].filter(Boolean);
|
|
2043
|
-
return {
|
|
2044
|
-
ok: true,
|
|
2045
|
-
widget_type: "chart",
|
|
2046
|
-
title: chart.title,
|
|
2047
|
-
subtitle: chart.showDescription ? chart.subtitle : undefined,
|
|
2048
|
-
source: chartWidgetSource(source, sourceQuery, queryText, snapshot),
|
|
2049
|
-
result_table: {
|
|
2050
|
-
columns,
|
|
2051
|
-
row_count: rows.length,
|
|
2052
|
-
rows: rows.map((row) => Object.fromEntries(columns.map((column) => [column.key, row[column.key]]))),
|
|
2053
|
-
truncated: false
|
|
2054
|
-
},
|
|
2055
|
-
visualization_spec: {
|
|
2056
|
-
version: "1",
|
|
2057
|
-
intent: chart.intent ?? "custom",
|
|
2058
|
-
visualization_type: chart.type,
|
|
2059
|
-
encodings: {
|
|
2060
|
-
x: { field: xField, type: chartEncoding(chart, "x").type ?? "nominal" },
|
|
2061
|
-
y: { aggregate: chartEncoding(chart, "y").aggregate ?? "sum", field: yField, type: "quantitative", unit },
|
|
2062
|
-
...(colorField ? { color: { field: colorField, type: chartEncoding(chart, "color").type ?? "nominal" } } : {})
|
|
2063
|
-
},
|
|
2064
|
-
presentation: {
|
|
2065
|
-
show_controls: true,
|
|
2066
|
-
unit,
|
|
2067
|
-
view_mode: "both"
|
|
2068
|
-
},
|
|
2069
|
-
settings
|
|
2070
|
-
}
|
|
2071
|
-
};
|
|
2072
|
-
}
|
|
2073
|
-
if (xField && yFields.length) {
|
|
2074
|
-
const longRows = rows.flatMap((row) => yFields.map((field) => ({
|
|
2075
|
-
[xField]: row[xField],
|
|
2076
|
-
series: field,
|
|
2077
|
-
value: row[field]
|
|
2078
|
-
})));
|
|
2079
|
-
return {
|
|
2080
|
-
ok: true,
|
|
2081
|
-
widget_type: "chart",
|
|
2082
|
-
title: chart.title,
|
|
2083
|
-
subtitle: chart.showDescription ? chart.subtitle : undefined,
|
|
2084
|
-
source: chartWidgetSource(source, sourceQuery, queryText, snapshot),
|
|
2085
|
-
result_table: {
|
|
2086
|
-
columns: [
|
|
2087
|
-
{ key: xField, label: chartEncodingLabel(chart, "x", xField), type: chartWidgetColumnType(chartEncoding(chart, "x"), rows[0]?.[xField]) },
|
|
2088
|
-
{ key: "series", label: "Series", type: "text" },
|
|
2089
|
-
{ key: "value", label: chartEncodingLabel(chart, "y", "Value"), type: "number", unit }
|
|
2090
|
-
],
|
|
2091
|
-
row_count: longRows.length,
|
|
2092
|
-
rows: longRows,
|
|
2093
|
-
truncated: false
|
|
2094
|
-
},
|
|
2095
|
-
visualization_spec: {
|
|
2096
|
-
version: "1",
|
|
2097
|
-
intent: chart.intent ?? "custom",
|
|
2098
|
-
visualization_type: chart.type,
|
|
2099
|
-
encodings: {
|
|
2100
|
-
x: { field: xField, type: chartEncoding(chart, "x").type ?? "nominal" },
|
|
2101
|
-
y: { aggregate: "sum", field: "value", type: "quantitative", unit },
|
|
2102
|
-
color: { field: "series", type: "nominal" }
|
|
2103
|
-
},
|
|
2104
|
-
presentation: {
|
|
2105
|
-
show_controls: true,
|
|
2106
|
-
unit,
|
|
2107
|
-
view_mode: "both"
|
|
2108
|
-
},
|
|
2109
|
-
settings
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
2112
|
-
}
|
|
2113
|
-
return null;
|
|
2114
|
-
}
|
|
2115
|
-
function chartWidgetSettings(chart) {
|
|
2116
|
-
const source = chart?.settings && typeof chart.settings === "object" && !Array.isArray(chart.settings) ? chart.settings : {};
|
|
2117
|
-
const type = chart?.type;
|
|
2118
|
-
const orientation = source.orientation === "horizontal" || type === "horizontalBar" || type === "horizontalStackedBar" || type === "horizontalStackedBar100"
|
|
2119
|
-
? "horizontal"
|
|
2120
|
-
: source.orientation === "vertical"
|
|
2121
|
-
? "vertical"
|
|
2122
|
-
: undefined;
|
|
2123
|
-
const groupMode = source.groupMode ?? source.group_mode ?? (type === "stackedBar100" || type === "horizontalStackedBar100"
|
|
2124
|
-
? "stacked100"
|
|
2125
|
-
: type === "stackedBar" || type === "horizontalStackedBar"
|
|
2126
|
-
? "stacked"
|
|
2127
|
-
: undefined);
|
|
2128
|
-
return {
|
|
2129
|
-
...(orientation ? { orientation } : {}),
|
|
2130
|
-
...(["grouped", "stacked", "stacked100"].includes(groupMode) ? { group_mode: groupMode } : {})
|
|
2131
|
-
};
|
|
2132
|
-
}
|
|
2133
|
-
function chartWidgetPayload(chart, rows, source, snapshot, sourceQueryText) {
|
|
2134
|
-
const encodedPayload = chartHasEncodingSpec(chart) ? chartWidgetPayloadFromEncodings(chart, rows, source, snapshot, sourceQueryText) : null;
|
|
2135
|
-
if (encodedPayload)
|
|
2136
|
-
return encodedPayload;
|
|
2137
|
-
return null;
|
|
2138
|
-
}
|
|
2139
|
-
function TableContent({ allowColumnResize = true, columnWidths, density, filters, onColumnWidthsChange, selectedFilters, snapshot, table, isFullscreen = false }) {
|
|
2140
|
-
const rows = filterRowsForDataset(getRows(snapshot, table.dataset), table.dataset, filters, selectedFilters, table.columns.map((column) => column.field));
|
|
2141
|
-
const [page, setPage] = useState(0);
|
|
2142
|
-
const [sortState, setSortState] = useState(null);
|
|
2143
|
-
const [isColumnResizing, setIsColumnResizing] = useState(false);
|
|
2144
|
-
const headerCellRefs = useRef({});
|
|
2145
|
-
const tableWrapRef = useRef(null);
|
|
2146
|
-
const sortedRows = useMemo(() => {
|
|
2147
|
-
if (!sortState)
|
|
2148
|
-
return rows;
|
|
2149
|
-
return [...rows].sort((a, b) => compareTableValues(a, b, sortState.field, sortState.direction));
|
|
2150
|
-
}, [rows, sortState]);
|
|
2151
|
-
const pageSize = isFullscreen ? Math.max(1, sortedRows.length) : TABLE_CARD_PAGE_SIZE;
|
|
2152
|
-
const totalPages = Math.max(1, Math.ceil(sortedRows.length / pageSize));
|
|
2153
|
-
const currentPage = Math.min(page, totalPages - 1);
|
|
2154
|
-
const shouldShowPagination = rows.length > 0 && !isFullscreen && totalPages > 1;
|
|
2155
|
-
const shouldShowCount = rows.length > 0 && shouldShowPagination;
|
|
2156
|
-
const shouldShowFooter = shouldShowCount || shouldShowPagination;
|
|
2157
|
-
const resultCountLabel = `${sortedRows.length.toLocaleString()} ${sortedRows.length === 1 ? "result" : "results"}`;
|
|
2158
|
-
const visibleRows = isFullscreen
|
|
2159
|
-
? sortedRows
|
|
2160
|
-
: sortedRows.slice(currentPage * pageSize, currentPage * pageSize + pageSize);
|
|
2161
|
-
const estimatedColumnWidths = useMemo(() => estimateTableColumnWidths(table, rows, density), [density, rows, table]);
|
|
2162
|
-
const activeColumnWidths = useMemo(() => {
|
|
2163
|
-
return Object.fromEntries(table.columns.map((column) => [
|
|
2164
|
-
column.field,
|
|
2165
|
-
clamp(Math.round(columnWidths[column.field] ?? estimatedColumnWidths[column.field] ?? TABLE_COLUMN_DEFAULT_WIDTH), TABLE_COLUMN_MIN_WIDTH, TABLE_COLUMN_MAX_WIDTH)
|
|
2166
|
-
]));
|
|
2167
|
-
}, [columnWidths, estimatedColumnWidths, table.columns]);
|
|
2168
|
-
const tablePixelWidth = table.columns.reduce((total, column) => total + activeColumnWidths[column.field], 0);
|
|
2169
|
-
const tableStyle = {
|
|
2170
|
-
minWidth: `${tablePixelWidth}px`,
|
|
2171
|
-
tableLayout: "fixed",
|
|
2172
|
-
width: `${tablePixelWidth}px`
|
|
2173
|
-
};
|
|
2174
|
-
useEffect(() => {
|
|
2175
|
-
setPage(0);
|
|
2176
|
-
}, [isFullscreen, rows.length, sortState]);
|
|
2177
|
-
useLayoutEffect(() => {
|
|
2178
|
-
if (tableWrapRef.current) {
|
|
2179
|
-
tableWrapRef.current.scrollLeft = 0;
|
|
2180
|
-
}
|
|
2181
|
-
}, [isFullscreen, rows.length, table.id]);
|
|
2182
|
-
function toggleSort(field) {
|
|
2183
|
-
setSortState((current) => {
|
|
2184
|
-
if (current?.field !== field)
|
|
2185
|
-
return { direction: "asc", field };
|
|
2186
|
-
return { direction: current.direction === "asc" ? "desc" : "asc", field };
|
|
2187
|
-
});
|
|
2188
|
-
}
|
|
2189
|
-
function measuredColumnWidths() {
|
|
2190
|
-
return Object.fromEntries(table.columns.map((column) => {
|
|
2191
|
-
const measuredWidth = headerCellRefs.current[column.field]?.getBoundingClientRect().width;
|
|
2192
|
-
const width = measuredWidth ?? activeColumnWidths[column.field] ?? TABLE_COLUMN_DEFAULT_WIDTH;
|
|
2193
|
-
return [
|
|
2194
|
-
column.field,
|
|
2195
|
-
clamp(Math.round(width), TABLE_COLUMN_MIN_WIDTH, TABLE_COLUMN_MAX_WIDTH)
|
|
2196
|
-
];
|
|
2197
|
-
}));
|
|
2198
|
-
}
|
|
2199
|
-
function resizeColumnBy(field, delta, shouldPersist) {
|
|
2200
|
-
const baseWidths = { ...activeColumnWidths, ...measuredColumnWidths(), ...columnWidths };
|
|
2201
|
-
const currentWidth = baseWidths[field] ?? TABLE_COLUMN_DEFAULT_WIDTH;
|
|
2202
|
-
onColumnWidthsChange(table.id, {
|
|
2203
|
-
...baseWidths,
|
|
2204
|
-
[field]: clamp(Math.round(currentWidth + delta), TABLE_COLUMN_MIN_WIDTH, TABLE_COLUMN_MAX_WIDTH)
|
|
2205
|
-
}, { persist: shouldPersist });
|
|
2206
|
-
}
|
|
2207
|
-
function startColumnResize(event, field) {
|
|
2208
|
-
event.preventDefault();
|
|
2209
|
-
event.stopPropagation();
|
|
2210
|
-
const baseWidths = { ...activeColumnWidths, ...measuredColumnWidths(), ...columnWidths };
|
|
2211
|
-
const startWidth = baseWidths[field] ?? TABLE_COLUMN_DEFAULT_WIDTH;
|
|
2212
|
-
const startX = event.clientX;
|
|
2213
|
-
let latestWidths = baseWidths;
|
|
2214
|
-
onColumnWidthsChange(table.id, baseWidths, { persist: false });
|
|
2215
|
-
setIsColumnResizing(true);
|
|
2216
|
-
document.body.style.cursor = "col-resize";
|
|
2217
|
-
document.body.style.userSelect = "none";
|
|
2218
|
-
function handlePointerMove(moveEvent) {
|
|
2219
|
-
const nextWidth = clamp(Math.round(startWidth + moveEvent.clientX - startX), TABLE_COLUMN_MIN_WIDTH, TABLE_COLUMN_MAX_WIDTH);
|
|
2220
|
-
latestWidths = { ...baseWidths, [field]: nextWidth };
|
|
2221
|
-
onColumnWidthsChange(table.id, latestWidths, { persist: false });
|
|
2222
|
-
}
|
|
2223
|
-
function finishResize() {
|
|
2224
|
-
window.removeEventListener("pointermove", handlePointerMove);
|
|
2225
|
-
window.removeEventListener("pointerup", finishResize);
|
|
2226
|
-
window.removeEventListener("pointercancel", finishResize);
|
|
2227
|
-
document.body.style.cursor = "";
|
|
2228
|
-
document.body.style.userSelect = "";
|
|
2229
|
-
setIsColumnResizing(false);
|
|
2230
|
-
onColumnWidthsChange(table.id, latestWidths, { persist: true });
|
|
2231
|
-
}
|
|
2232
|
-
window.addEventListener("pointermove", handlePointerMove);
|
|
2233
|
-
window.addEventListener("pointerup", finishResize);
|
|
2234
|
-
window.addEventListener("pointercancel", finishResize);
|
|
2235
|
-
}
|
|
2236
|
-
function handleResizeHandleKeyDown(event, field) {
|
|
2237
|
-
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
|
|
2238
|
-
return;
|
|
2239
|
-
event.preventDefault();
|
|
2240
|
-
event.stopPropagation();
|
|
2241
|
-
resizeColumnBy(field, event.key === "ArrowRight" ? TABLE_COLUMN_KEYBOARD_STEP : -TABLE_COLUMN_KEYBOARD_STEP, true);
|
|
2242
|
-
}
|
|
2243
|
-
return (<>
|
|
2244
|
-
{rows.length ? (<div className={`table-wrap table-density-${density} ${isFullscreen ? "fullscreen" : ""}`} ref={tableWrapRef}>
|
|
2245
|
-
<div className="table-scroll-content">
|
|
2246
|
-
<table className={`data-table data-table-${density} data-table-resizable ${isColumnResizing ? "is-column-resizing" : ""}`} style={tableStyle}>
|
|
2247
|
-
<colgroup>
|
|
2248
|
-
{table.columns.map((column) => (<col key={column.field} style={{ width: `${activeColumnWidths[column.field]}px` }}/>))}
|
|
2249
|
-
</colgroup>
|
|
2250
|
-
<thead>
|
|
2251
|
-
<tr>
|
|
2252
|
-
{table.columns.map((column) => {
|
|
2253
|
-
const isSorted = sortState?.field === column.field;
|
|
2254
|
-
const format = tableColumnFormat(column);
|
|
2255
|
-
const isNumericColumn = isNumericTableColumn(column, rows);
|
|
2256
|
-
const isCenteredColumn = column.align === "center";
|
|
2257
|
-
const SortIcon = isSorted ? (sortState.direction === "asc" ? ArrowUp : ArrowDown) : null;
|
|
2258
|
-
return (<th aria-sort={isSorted
|
|
2259
|
-
? sortState.direction === "asc"
|
|
2260
|
-
? "ascending"
|
|
2261
|
-
: "descending"
|
|
2262
|
-
: "none"} key={column.field} ref={(element) => {
|
|
2263
|
-
headerCellRefs.current[column.field] = element;
|
|
2264
|
-
}} className={isNumericColumn ? "table-header-number" : isCenteredColumn ? "center" : undefined} style={{ width: `${activeColumnWidths[column.field]}px` }}>
|
|
2265
|
-
<button className="table-sort-button" onClick={() => toggleSort(column.field)} type="button">
|
|
2266
|
-
<span>{column.label}</span>
|
|
2267
|
-
{SortIcon ? <SortIcon aria-hidden="true" size={14} strokeWidth={2}/> : null}
|
|
2268
|
-
</button>
|
|
2269
|
-
{allowColumnResize ? (<button aria-label={`Resize ${column.label} column`} className="table-column-resize-handle" onKeyDown={(event) => handleResizeHandleKeyDown(event, column.field)} onPointerDown={(event) => startColumnResize(event, column.field)} title={`Resize ${column.label} column`} type="button"/>) : null}
|
|
2270
|
-
</th>);
|
|
2271
|
-
})}
|
|
2272
|
-
</tr>
|
|
2273
|
-
</thead>
|
|
2274
|
-
<tbody>
|
|
2275
|
-
{visibleRows.map((row, rowIndex) => (<tr key={`${currentPage}-${rowIndex}`}>
|
|
2276
|
-
{table.columns.map((column) => {
|
|
2277
|
-
const value = row[column.field];
|
|
2278
|
-
const format = tableColumnFormat(column);
|
|
2279
|
-
const isNumericColumn = isNumericTableColumn(column, rows);
|
|
2280
|
-
const isCenteredColumn = column.align === "center";
|
|
2281
|
-
const className = [
|
|
2282
|
-
isNumericColumn ? "table-cell-number" : "",
|
|
2283
|
-
isCenteredColumn ? "center" : "",
|
|
2284
|
-
tableCellMovementClass(column, value)
|
|
2285
|
-
].filter(Boolean).join(" ") || undefined;
|
|
2286
|
-
return (<td className={className} key={column.field}>
|
|
2287
|
-
{formatTableCellValue(column, value)}
|
|
2288
|
-
</td>);
|
|
2289
|
-
})}
|
|
2290
|
-
</tr>))}
|
|
2291
|
-
</tbody>
|
|
2292
|
-
</table>
|
|
2293
|
-
</div>
|
|
2294
|
-
</div>) : (<div className="empty-state">No rows match the selected filters.</div>)}
|
|
2295
|
-
{shouldShowFooter ? (<div className="table-pagination">
|
|
2296
|
-
{shouldShowCount ? <span className="table-result-count">{resultCountLabel}</span> : null}
|
|
2297
|
-
{shouldShowPagination ? (<div className="table-page-control">
|
|
2298
|
-
<span>
|
|
2299
|
-
Page {currentPage + 1} of {totalPages}
|
|
2300
|
-
</span>
|
|
2301
|
-
<div className="table-page-buttons">
|
|
2302
|
-
<button aria-label="Previous page" className="table-arrow-button" disabled={currentPage === 0} onClick={() => setPage((nextPage) => Math.max(0, nextPage - 1))} type="button">
|
|
2303
|
-
<ChevronLeft aria-hidden="true" size={16} strokeWidth={2}/>
|
|
2304
|
-
</button>
|
|
2305
|
-
<button aria-label="Next page" className="table-arrow-button" disabled={currentPage >= totalPages - 1} onClick={() => setPage((nextPage) => Math.min(totalPages - 1, nextPage + 1))} type="button">
|
|
2306
|
-
<ChevronRight aria-hidden="true" size={16} strokeWidth={2}/>
|
|
2307
|
-
</button>
|
|
2308
|
-
</div>
|
|
2309
|
-
</div>) : null}
|
|
2310
|
-
</div>) : null}
|
|
2311
|
-
</>);
|
|
2312
|
-
}
|
|
2313
|
-
function DataTable({ columnWidths, filters, isEditMode, isMenuOpen, layout, manifest, onColumnWidthsChange, onDeleteBlock, onMenuOpenChange, onModalOpen, onRequestEditMode, onTextChange, selectedFilters, snapshot, table, textOverride }) {
|
|
2314
|
-
const tableDensity = table.density ?? (manifest?.surface === "report" ? "spacious" : "dense");
|
|
2315
|
-
const displayHeaderMarkdown = textOverride?.headerMarkdown ?? composeTableHeaderMarkdown(table);
|
|
2316
|
-
const displayTitle = markdownFirstLine(displayHeaderMarkdown, table.title);
|
|
2317
|
-
const { closeMenu, fixedMenuStyle, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isMenuOpen, onMenuOpenChange);
|
|
2318
|
-
const menuItem = (label, icon, onClick, tone = "default") => (<button className={`viz-card-menu-item ${tone === "danger" ? "viz-card-menu-item-danger" : ""}`.trim()} key={label} onClick={() => {
|
|
2319
|
-
onClick();
|
|
2320
|
-
closeMenu();
|
|
2321
|
-
}} role="menuitem" type="button">
|
|
2322
|
-
<span aria-hidden="true" className="viz-card-menu-icon">
|
|
2323
|
-
{icon}
|
|
2324
|
-
</span>
|
|
2325
|
-
<span>{label}</span>
|
|
2326
|
-
</button>);
|
|
2327
|
-
return (<section className={`panel table-panel table-card ${layout === "half" ? "" : "layout-full"}`}>
|
|
2328
|
-
<PanelHeader action={<div className="viz-card-actions" data-image-export-exclude="true" ref={menuRef}>
|
|
2329
|
-
<button aria-expanded={isMenuOpen} aria-label={`Open options for ${table.title}`} className="viz-card-menu-button viz-card__no-drag" onClick={(event) => {
|
|
2330
|
-
event.stopPropagation();
|
|
2331
|
-
toggleMenu();
|
|
2332
|
-
}} onKeyDown={handleMenuButtonKeyDown} ref={menuButtonRef} type="button">
|
|
2333
|
-
<Ellipsis aria-hidden="true" size={18} strokeWidth={2}/>
|
|
2334
|
-
</button>
|
|
2335
|
-
{shouldRenderMenu ? (<div className={`viz-card-menu menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu" style={fixedMenuStyle}>
|
|
2336
|
-
{menuItem("View data source", <Database size={18} strokeWidth={2}/>, () => onModalOpen({ kind: "source", table }))}
|
|
2337
|
-
{menuItem("View fullscreen", <Expand size={18} strokeWidth={2}/>, () => onModalOpen({ kind: "fullscreen", table }))}
|
|
2338
|
-
{onDeleteBlock ? menuItem("Delete", <Trash2 size={18} strokeWidth={2}/>, onDeleteBlock, "danger") : null}
|
|
2339
|
-
</div>) : null}
|
|
2340
|
-
</div>} subtitle={table.subtitle} title={displayTitle} titleRowClassName="table-card__drag-handle">
|
|
2341
|
-
<RichMarkdown ariaLabel={`Edit markdown header for ${table.title}`} className="editable-cell-header" isEditMode={isEditMode} markdown={displayHeaderMarkdown} onMarkdownChange={(nextMarkdown) => onTextChange(table.id, { headerMarkdown: nextMarkdown })} onRequestEditMode={onRequestEditMode} placeholder={composeTableHeaderMarkdown(table)} variant="cellHeader"/>
|
|
2342
|
-
</PanelHeader>
|
|
2343
|
-
<TableContent columnWidths={columnWidths} density={tableDensity} filters={filters} onColumnWidthsChange={onColumnWidthsChange} selectedFilters={selectedFilters} snapshot={snapshot} table={table}/>
|
|
2344
|
-
</section>);
|
|
2345
|
-
}
|
|
2346
|
-
function TableModalDialog({ activeFilters, columnWidths, filters, kind, manifest, onColumnWidthsChange, onClose, selectedFilters, snapshot, table }) {
|
|
2347
|
-
const dialogRef = useRef(null);
|
|
2348
|
-
const source = sourceForTable(table, manifest?.sources ?? []);
|
|
2349
|
-
const title = kind === "fullscreen" ? table.title : "Data source";
|
|
2350
|
-
const sourceQuery = sourceQueryFromSourceSpec(source);
|
|
2351
|
-
const previewRows = filterRowsForDataset(getRows(snapshot, table.dataset), table.dataset, filters, selectedFilters, table.columns.map((column) => column.field));
|
|
2352
|
-
const buildDetails = sourceBuildDetails({
|
|
2353
|
-
activeFilters,
|
|
2354
|
-
columns: table.columns,
|
|
2355
|
-
dataset: table.dataset,
|
|
2356
|
-
source,
|
|
2357
|
-
sourceQuery,
|
|
2358
|
-
snapshot
|
|
2359
|
-
});
|
|
2360
|
-
useModalScrollLock(kind === "source");
|
|
2361
|
-
useEffect(() => {
|
|
2362
|
-
const dialog = dialogRef.current;
|
|
2363
|
-
if (dialog && !dialog.open) {
|
|
2364
|
-
dialog.showModal();
|
|
2365
|
-
}
|
|
2366
|
-
}, []);
|
|
2367
|
-
return (<dialog aria-labelledby="table-modal-title" className={`native-modal ${kind === "source" ? "source-modal" : ""}`.trim()} onCancel={onClose} onClick={(event) => {
|
|
2368
|
-
if (event.target === event.currentTarget) {
|
|
2369
|
-
event.currentTarget.close();
|
|
2370
|
-
}
|
|
2371
|
-
}} onClose={onClose} ref={dialogRef}>
|
|
2372
|
-
<section className={`modal-panel ${kind === "source" ? "source-modal-panel" : ""}`.trim()}>
|
|
2373
|
-
<div className="modal-header">
|
|
2374
|
-
<div>
|
|
2375
|
-
<h2 id="table-modal-title">{title}</h2>
|
|
2376
|
-
{kind === "fullscreen" && source?.label ? <p>{source.label}</p> : null}
|
|
2377
|
-
</div>
|
|
2378
|
-
<button aria-label={`Close ${kind === "source" ? "data source" : "fullscreen table"}`} className="modal-close-button" onClick={() => dialogRef.current?.close()} type="button">
|
|
2379
|
-
<X aria-hidden="true" size={20} strokeWidth={2}/>
|
|
2380
|
-
</button>
|
|
2381
|
-
</div>
|
|
2382
|
-
{kind === "fullscreen" ? (<TableContent columnWidths={columnWidths} density={table.density ?? (manifest?.surface === "report" ? "spacious" : "dense")} filters={filters} isFullscreen onColumnWidthsChange={onColumnWidthsChange} selectedFilters={selectedFilters} snapshot={snapshot} table={table}/>) : (<DataSourceDetails details={buildDetails} source={source} sourceQuery={sourceQuery}>
|
|
2383
|
-
<SourceDataTable columns={table.columns} dataset={table.dataset} density={table.density ?? (manifest?.surface === "report" ? "spacious" : "dense")} rows={previewRows}/>
|
|
2384
|
-
</DataSourceDetails>)}
|
|
2385
|
-
</section>
|
|
2386
|
-
</dialog>);
|
|
2387
|
-
}
|
|
2388
|
-
function ReportTextBlock({ block, isEditMode, isMenuOpen, onCopyResult, onDeleteBlock, onMenuOpenChange, onRequestEditMode, onTextChange, textOverride }) {
|
|
2389
|
-
const cardRef = useRef(null);
|
|
2390
|
-
const toneClass = `report-block-${block.type}`;
|
|
2391
|
-
const blockMarkdown = composeBlockMarkdown(block, textOverride);
|
|
2392
|
-
const { closeMenu, fixedMenuStyle, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isMenuOpen, onMenuOpenChange);
|
|
2393
|
-
const { getPreparedImageBlob, preparedImageExportStatus, prepareImageExport, resetPreparedImageExport } = usePreparedImageExport(cardRef);
|
|
2394
|
-
async function handleCopyMarkdown() {
|
|
2395
|
-
try {
|
|
2396
|
-
await copyTextToClipboard(blockMarkdown.trim());
|
|
2397
|
-
onCopyResult("Copied markdown.");
|
|
2398
|
-
}
|
|
2399
|
-
catch (error) {
|
|
2400
|
-
onCopyResult(error instanceof Error ? error.message : "Failed to copy markdown.", true);
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
function prepareImageExportQuietly(force = false) {
|
|
2404
|
-
if (!shouldOfferImageClipboardCopy())
|
|
2405
|
-
return;
|
|
2406
|
-
const prepared = prepareImageExport({ force });
|
|
2407
|
-
if (prepared)
|
|
2408
|
-
void prepared.promise.catch(() => undefined);
|
|
2409
|
-
}
|
|
2410
|
-
async function handleCopyAsImage() {
|
|
2411
|
-
if (!cardRef.current)
|
|
2412
|
-
return;
|
|
2413
|
-
try {
|
|
2414
|
-
const copyResult = await copyElementAsImage(cardRef.current, getPreparedImageBlob());
|
|
2415
|
-
resetPreparedImageExport();
|
|
2416
|
-
onCopyResult(imageCopySuccessMessage("Copied text block as image.", copyResult));
|
|
2417
|
-
}
|
|
2418
|
-
catch (error) {
|
|
2419
|
-
onCopyResult(error instanceof Error ? error.message : "Failed to copy image.", true);
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2422
|
-
const menuItem = (label, icon, onClick, tone = "default", onPrepare, disabled = false) => (<button className={`viz-card-menu-item ${tone === "danger" ? "viz-card-menu-item-danger" : ""}`.trim()} disabled={disabled} key={label} onFocus={onPrepare} onClick={() => {
|
|
2423
|
-
void onClick();
|
|
2424
|
-
closeMenu();
|
|
2425
|
-
}} onPointerEnter={onPrepare} role="menuitem" type="button">
|
|
2426
|
-
<span aria-hidden="true" className="viz-card-menu-icon">
|
|
2427
|
-
{icon}
|
|
2428
|
-
</span>
|
|
2429
|
-
<span>{label}</span>
|
|
2430
|
-
</button>);
|
|
2431
|
-
return (<section className={`panel report-block report-markdown-block ${toneClass}`} id={block.id} ref={cardRef}>
|
|
2432
|
-
<div className="viz-card-actions" data-image-export-exclude="true" ref={menuRef}>
|
|
2433
|
-
<button aria-expanded={isMenuOpen} aria-label={`Open options for ${markdownFirstLine(blockMarkdown, "Text block")}`} className="viz-card-menu-button viz-card__no-drag" onClick={(event) => {
|
|
2434
|
-
event.stopPropagation();
|
|
2435
|
-
const nextIsMenuOpen = toggleMenu();
|
|
2436
|
-
if (nextIsMenuOpen) {
|
|
2437
|
-
prepareImageExportQuietly(true);
|
|
2438
|
-
}
|
|
2439
|
-
else {
|
|
2440
|
-
resetPreparedImageExport();
|
|
2441
|
-
}
|
|
2442
|
-
}} onFocus={() => prepareImageExportQuietly()} onKeyDown={handleMenuButtonKeyDown} onPointerEnter={() => prepareImageExportQuietly()} ref={menuButtonRef} type="button">
|
|
2443
|
-
<Ellipsis aria-hidden="true" size={18} strokeWidth={2}/>
|
|
2444
|
-
</button>
|
|
2445
|
-
{shouldRenderMenu ? (<div className={`viz-card-menu viz-card__no-drag menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu" style={fixedMenuStyle}>
|
|
2446
|
-
{menuItem("Copy markdown", <Copy size={18} strokeWidth={2}/>, handleCopyMarkdown)}
|
|
2447
|
-
{shouldOfferImageClipboardCopy()
|
|
2448
|
-
? menuItem("Copy as image", <Camera size={18} strokeWidth={2}/>, handleCopyAsImage, "default", prepareImageExportQuietly, preparedImageExportStatus === "pending")
|
|
2449
|
-
: null}
|
|
2450
|
-
{menuItem("Delete", <Trash2 size={18} strokeWidth={2}/>, onDeleteBlock, "danger")}
|
|
2451
|
-
</div>) : null}
|
|
2452
|
-
</div>
|
|
2453
|
-
<div className="report-block-body markdown-body">
|
|
2454
|
-
<RichMarkdown ariaLabel={`Edit markdown for ${block.type}`} className="report-markdown-editor" isEditMode={isEditMode} markdown={blockMarkdown} minRows={Math.max(2, Math.min(6, blockMarkdown.split(/\r?\n/).length))} onMarkdownChange={(nextMarkdown) => onTextChange(block.id, { bodyMarkdown: nextMarkdown })} onRequestEditMode={onRequestEditMode} placeholder={composeBlockMarkdown(block)} variant="reportBlock"/>
|
|
2455
|
-
</div>
|
|
2456
|
-
</section>);
|
|
2457
|
-
}
|
|
2458
|
-
function ReportHtmlBlock({ block, htmlOverride, isEditMode, isMenuOpen, onHtmlChange, onCopyResult, onDeleteBlock, onMenuOpenChange }) {
|
|
2459
|
-
const html = composeBlockHtml(block, htmlOverride);
|
|
2460
|
-
const title = block.id || "HTML block";
|
|
2461
|
-
const { closeMenu, fixedMenuStyle, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isMenuOpen, onMenuOpenChange);
|
|
2462
|
-
const frameRef = useRef(null);
|
|
2463
|
-
const resizeObserverRef = useRef(null);
|
|
2464
|
-
const [frameHeight, setFrameHeight] = useState(0);
|
|
2465
|
-
const updateFrameHeight = useCallback(() => {
|
|
2466
|
-
const nextHeight = measureHtmlFrameHeight(frameRef.current);
|
|
2467
|
-
if (nextHeight > 0)
|
|
2468
|
-
setFrameHeight(nextHeight);
|
|
2469
|
-
}, []);
|
|
2470
|
-
const handleFrameLoad = useCallback(() => {
|
|
2471
|
-
resizeObserverRef.current?.disconnect();
|
|
2472
|
-
const frameDocument = frameRef.current?.contentDocument;
|
|
2473
|
-
if (frameDocument && typeof ResizeObserver !== "undefined") {
|
|
2474
|
-
const observer = new ResizeObserver(updateFrameHeight);
|
|
2475
|
-
if (frameDocument.documentElement)
|
|
2476
|
-
observer.observe(frameDocument.documentElement);
|
|
2477
|
-
if (frameDocument.body)
|
|
2478
|
-
observer.observe(frameDocument.body);
|
|
2479
|
-
resizeObserverRef.current = observer;
|
|
2480
|
-
}
|
|
2481
|
-
updateFrameHeight();
|
|
2482
|
-
}, [updateFrameHeight]);
|
|
2483
|
-
useEffect(() => {
|
|
2484
|
-
setFrameHeight(0);
|
|
2485
|
-
}, [html]);
|
|
2486
|
-
useEffect(() => {
|
|
2487
|
-
return () => resizeObserverRef.current?.disconnect();
|
|
2488
|
-
}, []);
|
|
2489
|
-
async function handleCopyHtml() {
|
|
2490
|
-
try {
|
|
2491
|
-
await copyTextToClipboard(html);
|
|
2492
|
-
onCopyResult("Copied HTML.");
|
|
2493
|
-
}
|
|
2494
|
-
catch (error) {
|
|
2495
|
-
onCopyResult(error instanceof Error ? error.message : "Failed to copy HTML.", true);
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
const menuItem = (label, icon, onClick, tone = "default") => (<button className={`viz-card-menu-item ${tone === "danger" ? "viz-card-menu-item-danger" : ""}`.trim()} key={label} onClick={() => {
|
|
2499
|
-
void onClick();
|
|
2500
|
-
closeMenu();
|
|
2501
|
-
}} role="menuitem" type="button">
|
|
2502
|
-
<span aria-hidden="true" className="viz-card-menu-icon">
|
|
2503
|
-
{icon}
|
|
2504
|
-
</span>
|
|
2505
|
-
<span>{label}</span>
|
|
2506
|
-
</button>);
|
|
2507
|
-
return (<section className="report-block report-html-block" id={block.id}>
|
|
2508
|
-
<div className="viz-card-actions" data-image-export-exclude="true" ref={menuRef}>
|
|
2509
|
-
<button aria-expanded={isMenuOpen} aria-label={`Open options for ${title}`} className="viz-card-menu-button viz-card__no-drag" onClick={(event) => {
|
|
2510
|
-
event.stopPropagation();
|
|
2511
|
-
toggleMenu();
|
|
2512
|
-
}} onKeyDown={handleMenuButtonKeyDown} ref={menuButtonRef} type="button">
|
|
2513
|
-
<Ellipsis aria-hidden="true" size={18} strokeWidth={2}/>
|
|
2514
|
-
</button>
|
|
2515
|
-
{shouldRenderMenu ? (<div className={`viz-card-menu viz-card__no-drag menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu" style={fixedMenuStyle}>
|
|
2516
|
-
{menuItem("Copy HTML", <Copy size={18} strokeWidth={2}/>, handleCopyHtml)}
|
|
2517
|
-
{menuItem("Delete", <Trash2 size={18} strokeWidth={2}/>, onDeleteBlock, "danger")}
|
|
2518
|
-
</div>) : null}
|
|
2519
|
-
</div>
|
|
2520
|
-
{isEditMode ? (<textarea aria-label={`Edit HTML for ${title}`} className="report-html-editor" onChange={(event) => onHtmlChange(block.id, { html: event.target.value })} spellCheck={false} value={html}/>) : (<iframe className="report-html-frame" onLoad={handleFrameLoad} ref={frameRef} sandbox="allow-same-origin" srcDoc={sandboxedReportHtml(html)} style={frameHeight ? { height: `${frameHeight}px` } : undefined} title={title}/>)}
|
|
2521
|
-
</section>);
|
|
2522
|
-
}
|
|
2523
|
-
function ReportMetricStripBlock({ cards, filters, id, isMenuOpen, onCopyResult, onDeleteBlock, onMenuOpenChange, selectedFilters, snapshot }) {
|
|
2524
|
-
const cardRef = useRef(null);
|
|
2525
|
-
const { closeMenu, fixedMenuStyle, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isMenuOpen, onMenuOpenChange);
|
|
2526
|
-
const { getPreparedImageBlob, preparedImageExportStatus, prepareImageExport, resetPreparedImageExport } = usePreparedImageExport(cardRef);
|
|
2527
|
-
function prepareImageExportQuietly(force = false) {
|
|
2528
|
-
if (!shouldOfferImageClipboardCopy())
|
|
2529
|
-
return;
|
|
2530
|
-
const prepared = prepareImageExport({ force });
|
|
2531
|
-
if (prepared)
|
|
2532
|
-
void prepared.promise.catch(() => undefined);
|
|
2533
|
-
}
|
|
2534
|
-
async function handleCopyAsImage() {
|
|
2535
|
-
if (!cardRef.current)
|
|
2536
|
-
return;
|
|
2537
|
-
try {
|
|
2538
|
-
const copyResult = await copyElementAsImage(cardRef.current, getPreparedImageBlob());
|
|
2539
|
-
resetPreparedImageExport();
|
|
2540
|
-
onCopyResult(imageCopySuccessMessage("Copied key metrics as image.", copyResult));
|
|
2541
|
-
}
|
|
2542
|
-
catch (error) {
|
|
2543
|
-
onCopyResult(error instanceof Error ? error.message : "Failed to copy image.", true);
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
const menuItem = (label, icon, onClick, tone = "default", onPrepare, disabled = false) => (<button className={`viz-card-menu-item ${tone === "danger" ? "viz-card-menu-item-danger" : ""}`.trim()} disabled={disabled} key={label} onFocus={onPrepare} onClick={() => {
|
|
2547
|
-
void onClick();
|
|
2548
|
-
closeMenu();
|
|
2549
|
-
}} onPointerEnter={onPrepare} role="menuitem" type="button">
|
|
2550
|
-
<span aria-hidden="true" className="viz-card-menu-icon">
|
|
2551
|
-
{icon}
|
|
2552
|
-
</span>
|
|
2553
|
-
<span>{label}</span>
|
|
2554
|
-
</button>);
|
|
2555
|
-
return (<section className="report-metric-strip-block" id={id} ref={cardRef}>
|
|
2556
|
-
<div className="viz-card-actions" data-image-export-exclude="true" ref={menuRef}>
|
|
2557
|
-
<button aria-expanded={isMenuOpen} aria-label="Open options for key metrics" className="viz-card-menu-button viz-card__no-drag" onClick={(event) => {
|
|
2558
|
-
event.stopPropagation();
|
|
2559
|
-
const nextIsMenuOpen = toggleMenu();
|
|
2560
|
-
if (nextIsMenuOpen) {
|
|
2561
|
-
prepareImageExportQuietly(true);
|
|
2562
|
-
}
|
|
2563
|
-
else {
|
|
2564
|
-
resetPreparedImageExport();
|
|
2565
|
-
}
|
|
2566
|
-
}} onFocus={() => prepareImageExportQuietly()} onKeyDown={handleMenuButtonKeyDown} onPointerEnter={() => prepareImageExportQuietly()} ref={menuButtonRef} type="button">
|
|
2567
|
-
<Ellipsis aria-hidden="true" size={18} strokeWidth={2}/>
|
|
2568
|
-
</button>
|
|
2569
|
-
{shouldRenderMenu ? (<div className={`viz-card-menu viz-card__no-drag menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu" style={fixedMenuStyle}>
|
|
2570
|
-
{shouldOfferImageClipboardCopy()
|
|
2571
|
-
? menuItem("Copy as image", <Camera size={18} strokeWidth={2}/>, handleCopyAsImage, "default", prepareImageExportQuietly, preparedImageExportStatus === "pending")
|
|
2572
|
-
: null}
|
|
2573
|
-
{menuItem("Delete", <Trash2 size={18} strokeWidth={2}/>, onDeleteBlock, "danger")}
|
|
2574
|
-
</div>) : null}
|
|
2575
|
-
</div>
|
|
2576
|
-
<KpiStrip cards={cards} filters={filters} selectedFilters={selectedFilters} snapshot={snapshot}/>
|
|
2577
|
-
</section>);
|
|
2578
|
-
}
|
|
2579
|
-
function ReportBlockCard({ accessIssues, block, blockTextOverride, chart, chartSpecOverride, chartTextOverride, chartTypeOverride, columnWidths, filters, isBlockMenuOpen, isChartMenuOpen, isEditMode, isTableMenuOpen, layout, manifest, onBlockMenuOpenChange, onBlockTextChange, onChartTypeChange, onChartMenuOpenChange, onColumnWidthsChange, onDeleteBlock, onChartModalOpen, onCopyResult, onRequestEditMode, onTableMenuOpenChange, onTableModalOpen, onTableTextChange, onTextChange, selectedFilters, snapshot, table, tableTextOverride }) {
|
|
2580
|
-
if (block.type === "html") {
|
|
2581
|
-
return (<ReportHtmlBlock block={block} htmlOverride={blockTextOverride} isEditMode={isEditMode} isMenuOpen={isBlockMenuOpen} onHtmlChange={onBlockTextChange} onCopyResult={onCopyResult} onDeleteBlock={onDeleteBlock} onMenuOpenChange={onBlockMenuOpenChange}/>);
|
|
2582
|
-
}
|
|
2583
|
-
if (block.type === "chart" && chart) {
|
|
2584
|
-
const overriddenChart = applyChartSpecOverride(chart, chartSpecOverride);
|
|
2585
|
-
const chartRows = filterRowsForDataset(getRows(snapshot, overriddenChart.dataset), overriddenChart.dataset, filters, selectedFilters, chartUsedFields(overriddenChart));
|
|
2586
|
-
const chartTypeOptions = compatibleChartTypesForArtifactCard(overriddenChart, chartRows);
|
|
2587
|
-
const requestedType = chartTypeOverride ?? overriddenChart.type;
|
|
2588
|
-
const activeType = chartTypeOptions.some((option) => option.type === requestedType)
|
|
2589
|
-
? requestedType
|
|
2590
|
-
: overriddenChart.type;
|
|
2591
|
-
const displayChart = withChartType(overriddenChart, activeType);
|
|
2592
|
-
const accessIssue = accessIssueForChart(displayChart, accessIssues);
|
|
2593
|
-
return (<VizCard accessIssue={accessIssue} chart={displayChart} chartTypeOptions={chartTypeOptions} isEditMode={isEditMode} isMenuOpen={isChartMenuOpen} layout={layout} onChartTypeChange={onChartTypeChange} onCopyResult={onCopyResult} onDeleteBlock={onDeleteBlock} onMenuOpenChange={onChartMenuOpenChange} onModalOpen={onChartModalOpen} onRequestEditMode={onRequestEditMode} onTextChange={onTextChange} textOverride={chartTextOverride}>
|
|
2594
|
-
<ChartBody accessIssue={accessIssue} chart={displayChart} filters={filters} layout={layout} selectedFilters={selectedFilters} snapshot={snapshot}/>
|
|
2595
|
-
</VizCard>);
|
|
2596
|
-
}
|
|
2597
|
-
if (block.type === "table" && table) {
|
|
2598
|
-
return (<DataTable columnWidths={columnWidths} filters={filters} isEditMode={isEditMode} isMenuOpen={isTableMenuOpen} layout={layout} manifest={manifest} onColumnWidthsChange={onColumnWidthsChange} onDeleteBlock={onDeleteBlock} onMenuOpenChange={onTableMenuOpenChange} onModalOpen={onTableModalOpen} onRequestEditMode={onRequestEditMode} onTextChange={onTableTextChange} selectedFilters={selectedFilters} snapshot={snapshot} table={table} textOverride={tableTextOverride}/>);
|
|
2599
|
-
}
|
|
2600
|
-
return (<ReportTextBlock block={block} isEditMode={isEditMode} isMenuOpen={isBlockMenuOpen} onCopyResult={onCopyResult} onDeleteBlock={onDeleteBlock} onMenuOpenChange={onBlockMenuOpenChange} onRequestEditMode={onRequestEditMode} onTextChange={onBlockTextChange} textOverride={blockTextOverride}/>);
|
|
2601
|
-
}
|
|
2602
|
-
const EXPORT_TARGET_LABELS = {
|
|
2603
|
-
site: "Publish to Sites",
|
|
2604
|
-
html: "Create HTML file",
|
|
2605
|
-
pdf: "Create PDF",
|
|
2606
|
-
document: "Create Google Doc",
|
|
2607
|
-
slides: "Create Google Slides"
|
|
2608
|
-
};
|
|
2609
|
-
const EXPORT_TARGET_ICONS = {
|
|
2610
|
-
site: <Globe aria-hidden="true" size={16} strokeWidth={2}/>,
|
|
2611
|
-
html: <FileText aria-hidden="true" size={16} strokeWidth={2}/>,
|
|
2612
|
-
pdf: <FileDown aria-hidden="true" size={16} strokeWidth={2}/>,
|
|
2613
|
-
document: <FileText aria-hidden="true" size={16} strokeWidth={2}/>,
|
|
2614
|
-
slides: <Presentation aria-hidden="true" size={16} strokeWidth={2}/>
|
|
2615
|
-
};
|
|
2616
|
-
function packageControls(packageInfo) {
|
|
2617
|
-
return packageInfo?.controls && typeof packageInfo.controls === "object" ? packageInfo.controls : {};
|
|
2618
|
-
}
|
|
2619
|
-
function artifactCapabilities(packageInfo) {
|
|
2620
|
-
const controls = packageControls(packageInfo);
|
|
2621
|
-
const hostedReadOnly = packageInfo?.hostedReadOnly === true || packageInfo?.deliveryMode === "site_creator";
|
|
2622
|
-
return {
|
|
2623
|
-
canEdit: !hostedReadOnly && controls.edit !== false,
|
|
2624
|
-
canPublishHostedLink: !hostedReadOnly && controls.exportHostedLink !== false && controls.hostedLink !== false,
|
|
2625
|
-
canExportHtml: controls.html !== false,
|
|
2626
|
-
canExportPdf: controls.pdf !== false,
|
|
2627
|
-
canExportDocument: controls.document !== false,
|
|
2628
|
-
canExportSlides: controls.slides !== false
|
|
2629
|
-
};
|
|
2630
|
-
}
|
|
2631
|
-
function exportTargetsForCapabilities(capabilities) {
|
|
2632
|
-
return Object.keys(EXPORT_TARGET_LABELS).filter((target) => {
|
|
2633
|
-
if (target === "site")
|
|
2634
|
-
return capabilities.canPublishHostedLink;
|
|
2635
|
-
if (target === "html")
|
|
2636
|
-
return capabilities.canExportHtml;
|
|
2637
|
-
if (target === "pdf")
|
|
2638
|
-
return capabilities.canExportPdf;
|
|
2639
|
-
if (target === "document")
|
|
2640
|
-
return capabilities.canExportDocument;
|
|
2641
|
-
if (target === "slides")
|
|
2642
|
-
return capabilities.canExportSlides;
|
|
2643
|
-
return false;
|
|
2644
|
-
});
|
|
2645
|
-
}
|
|
2646
|
-
function appSurfaceLabel(manifest) {
|
|
2647
|
-
return manifest?.surface === "report" ? "report" : "dashboard";
|
|
2648
|
-
}
|
|
2649
|
-
function compactArtifactUrl() {
|
|
2650
|
-
try {
|
|
2651
|
-
const url = new URL(window.location.href);
|
|
2652
|
-
for (const name of ["locale", "deviceType", "unsafeSkipTargetOriginCheck"]) {
|
|
2653
|
-
url.searchParams.delete(name);
|
|
2654
|
-
}
|
|
2655
|
-
return url.toString();
|
|
2656
|
-
}
|
|
2657
|
-
catch {
|
|
2658
|
-
return window.location.href;
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
function usefulContextPath(value) {
|
|
2662
|
-
const trimmedValue = value?.trim();
|
|
2663
|
-
return trimmedValue && trimmedValue !== "tool payload" && !trimmedValue.startsWith("mcp://")
|
|
2664
|
-
? trimmedValue
|
|
2665
|
-
: null;
|
|
2666
|
-
}
|
|
2667
|
-
function promptContext(manifest, snapshot, packageInfo) {
|
|
2668
|
-
const packagePath = usefulContextPath(packageInfo?.root);
|
|
2669
|
-
const manifestPath = usefulContextPath(packageInfo?.manifestPath);
|
|
2670
|
-
const snapshotPath = usefulContextPath(packageInfo?.snapshotPath);
|
|
2671
|
-
return [
|
|
2672
|
-
`Artifact URL: ${compactArtifactUrl()}`,
|
|
2673
|
-
packagePath ? `Package path: ${packagePath}` : null,
|
|
2674
|
-
manifestPath ? `Manifest file: ${manifestPath}` : null,
|
|
2675
|
-
snapshotPath ? `Snapshot file: ${snapshotPath}` : null,
|
|
2676
|
-
`Generated at: ${snapshot?.generatedAt ?? manifest?.generatedAt ?? "unknown"}`
|
|
2677
|
-
]
|
|
2678
|
-
.filter((line) => Boolean(line))
|
|
2679
|
-
.join("\n");
|
|
2680
|
-
}
|
|
2681
|
-
function refreshPrompt(manifest, snapshot, packageInfo) {
|
|
2682
|
-
const surface = appSurfaceLabel(manifest);
|
|
2683
|
-
return `Refresh this ${surface} app using the artifact manifest's declared sources and the latest available time frame.
|
|
2684
|
-
|
|
2685
|
-
Inspect the artifact manifest for source details before rerunning. Update the snapshot data and generatedAt metadata, rebuild any static export, and verify the refreshed ${surface} in the in-app browser. Keep the current layout and narrative structure unless the latest data makes a statement incorrect.
|
|
2686
|
-
|
|
2687
|
-
${promptContext(manifest, snapshot, packageInfo)}`;
|
|
2688
|
-
}
|
|
2689
|
-
function exportPrompt(target, manifest, snapshot, packageInfo) {
|
|
2690
|
-
const surface = appSurfaceLabel(manifest);
|
|
2691
|
-
const context = promptContext(manifest, snapshot, packageInfo);
|
|
2692
|
-
if (target === "site") {
|
|
2693
|
-
return `Publish this ${surface} app as a hosted Site Creator link.
|
|
2694
|
-
|
|
2695
|
-
Use the current Data Analytics artifact as the source of truth. Create or reuse a Site Creator project for this ${surface}, materialize a Cloudflare Worker-compatible app that serves the current manifest, bounded snapshot, package metadata, and inline-safe source text through /api/manifest, /api/snapshot, /api/package, and /api/source-file, then deploy it through Site Creator. Preserve the rendered layout, charts, tables, source details, and narrative. Use the access scope I explicitly selected. If no access scope has been selected, keep the package local and ask for that publishing decision before deployment. Report the production URL and actual access mode only after successful deployment.
|
|
2696
|
-
|
|
2697
|
-
If the local package files are unavailable from the context below, stop and ask for the report package or validated artifact payload rather than publishing an empty or stale report.
|
|
2698
|
-
|
|
2699
|
-
${context}`;
|
|
2700
|
-
}
|
|
2701
|
-
if (target === "html") {
|
|
2702
|
-
return `Export this ${surface} app as a static, portable HTML artifact. Include the content title, narrative, charts, tables, and source details. Omit the interactive top bar and app-only controls from the exported artifact. Verify the HTML before delivery.
|
|
2703
|
-
|
|
2704
|
-
${context}`;
|
|
2705
|
-
}
|
|
2706
|
-
if (target === "document") {
|
|
2707
|
-
return `Use the existing data-analytics:report-to-google-doc workflow to export this ${surface} app as a polished native document artifact. Use the current app content and static HTML export when available, preserve headings, charts, tables, source details, and the executive narrative, then verify the created document.
|
|
2708
|
-
|
|
2709
|
-
${context}`;
|
|
2710
|
-
}
|
|
2711
|
-
if (target === "slides") {
|
|
2712
|
-
return `Use the existing data-analytics:report-to-google-slides workflow to export this ${surface} app as an executive-ready native slides artifact. Preserve the core claims, charts, tables, caveats, and source details, and verify the created deck.
|
|
2713
|
-
|
|
2714
|
-
${context}`;
|
|
2715
|
-
}
|
|
2716
|
-
return `Use the existing data-analytics:report-to-pdf workflow to create a PDF artifact from this ${surface} app. Use the static HTML/export path when available, preserve the content title, narrative, charts, tables, caveats, and source details, omit interactive top bars, share menus, edit controls, and app-only controls, then verify the PDF before delivery.
|
|
2717
|
-
|
|
2718
|
-
${context}`;
|
|
2719
|
-
}
|
|
2720
|
-
function setOptionalCodexSearchParam(url, name, value) {
|
|
2721
|
-
const trimmedValue = value?.trim();
|
|
2722
|
-
if (!trimmedValue)
|
|
2723
|
-
return;
|
|
2724
|
-
url.searchParams.set(name, trimmedValue);
|
|
2725
|
-
}
|
|
2726
|
-
function codexPromptUrl(prompt, packageInfo) {
|
|
2727
|
-
const url = new URL("codex://threads/new");
|
|
2728
|
-
setOptionalCodexSearchParam(url, "prompt", prompt);
|
|
2729
|
-
setOptionalCodexSearchParam(url, "originUrl", packageInfo?.originUrl);
|
|
2730
|
-
setOptionalCodexSearchParam(url, "path", packageInfo?.root);
|
|
2731
|
-
return url.toString();
|
|
2732
|
-
}
|
|
2733
|
-
function codexHostPromptPayload(prompt, packageInfo) {
|
|
2734
|
-
return {
|
|
2735
|
-
originUrl: packageInfo?.originUrl,
|
|
2736
|
-
path: packageInfo?.root,
|
|
2737
|
-
prompt
|
|
2738
|
-
};
|
|
2739
|
-
}
|
|
2740
|
-
function sendCodexPromptToHost(prompt, packageInfo) {
|
|
2741
|
-
const hostApi = window.openai;
|
|
2742
|
-
const payload = codexHostPromptPayload(prompt, packageInfo);
|
|
2743
|
-
const launchers = [hostApi?.sendFollowUpMessage, hostApi?.openCodexPrompt];
|
|
2744
|
-
for (const launch of launchers) {
|
|
2745
|
-
if (typeof launch !== "function")
|
|
2746
|
-
continue;
|
|
2747
|
-
try {
|
|
2748
|
-
void Promise.resolve(launch.call(hostApi, payload)).catch(() => { });
|
|
2749
|
-
return true;
|
|
2750
|
-
}
|
|
2751
|
-
catch {
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
return false;
|
|
2755
|
-
}
|
|
2756
|
-
function isLocalPreviewHost() {
|
|
2757
|
-
return ["localhost", "127.0.0.1", "::1"].includes(window.location.hostname);
|
|
2758
|
-
}
|
|
2759
|
-
function launchCodexDeepLink(url) {
|
|
2760
|
-
if (isLocalPreviewHost())
|
|
2761
|
-
return false;
|
|
2762
|
-
let linkClicked = false;
|
|
2763
|
-
try {
|
|
2764
|
-
const link = document.createElement("a");
|
|
2765
|
-
link.href = url;
|
|
2766
|
-
link.target = "_top";
|
|
2767
|
-
link.rel = "noopener noreferrer";
|
|
2768
|
-
link.style.display = "none";
|
|
2769
|
-
document.body.appendChild(link);
|
|
2770
|
-
link.click();
|
|
2771
|
-
link.remove();
|
|
2772
|
-
linkClicked = true;
|
|
2773
|
-
}
|
|
2774
|
-
catch {
|
|
2775
|
-
}
|
|
2776
|
-
try {
|
|
2777
|
-
if (window.top === window) {
|
|
2778
|
-
window.location.assign(url);
|
|
2779
|
-
return true;
|
|
2780
|
-
}
|
|
2781
|
-
}
|
|
2782
|
-
catch {
|
|
2783
|
-
}
|
|
2784
|
-
return linkClicked;
|
|
2785
|
-
}
|
|
2786
|
-
function openCodexPrompt(prompt, packageInfo, onCopyResult) {
|
|
2787
|
-
const url = codexPromptUrl(prompt, packageInfo);
|
|
2788
|
-
const launched = launchCodexDeepLink(url);
|
|
2789
|
-
const sentToHost = sendCodexPromptToHost(prompt, packageInfo);
|
|
2790
|
-
const openedCodex = sentToHost || launched;
|
|
2791
|
-
void copyTextToClipboard(prompt)
|
|
2792
|
-
.then(() => {
|
|
2793
|
-
onCopyResult(sentToHost
|
|
2794
|
-
? "Started in this chat."
|
|
2795
|
-
: openedCodex
|
|
2796
|
-
? "Codex prompt copied. Opening Codex if supported."
|
|
2797
|
-
: "Prompt copied. Open Codex and paste it to continue.", false);
|
|
2798
|
-
})
|
|
2799
|
-
.catch(() => {
|
|
2800
|
-
onCopyResult(openedCodex
|
|
2801
|
-
? "Opening Codex if supported, but prompt copy was blocked."
|
|
2802
|
-
: "Could not copy the Codex prompt from this preview.", true);
|
|
2803
|
-
});
|
|
2804
|
-
}
|
|
2805
|
-
function AnalyticsTopBarFreshness({ dateLabel, onRefresh, status, statusLabel }) {
|
|
2806
|
-
const refreshTitle = dateLabel === "Unknown"
|
|
2807
|
-
? "Refresh using latest available data."
|
|
2808
|
-
: `Refresh using latest available data. Last updated ${dateLabel}.`;
|
|
2809
|
-
const refreshLabel = dateLabel === "Unknown" ? "Refresh" : dateLabel;
|
|
2810
|
-
return (<div className="analytics-top-bar-freshness">
|
|
2811
|
-
<button aria-label={refreshTitle} className="top-bar-button top-bar-button-ghost top-bar-refresh-button" onClick={onRefresh} title={refreshTitle} type="button">
|
|
2812
|
-
<RefreshCw aria-hidden="true" size={14} strokeWidth={2}/>
|
|
2813
|
-
<span>{refreshLabel}</span>
|
|
2814
|
-
{statusLabel ? <span className={`snapshot-status ${status}`}>{statusLabel}</span> : null}
|
|
2815
|
-
</button>
|
|
2816
|
-
</div>);
|
|
2817
|
-
}
|
|
2818
|
-
function AnalyticsTopBar({ chrome = "full", isEditMode, onCancelEdit, manifest, onCopyResult, onEdit, onRequestFullscreen, onSaveEdit, onTitleChange, packageInfo, snapshot, title }) {
|
|
2819
|
-
const [isExportMenuOpen, setIsExportMenuOpen] = useState(false);
|
|
2820
|
-
const { closeMenu, fixedMenuStyle, handleMenuButtonKeyDown, handleMenuKeyDown, menuButtonRef, menuMotionClass, menuRef, toggleMenu, shouldRenderMenu } = useDashboardMenu(isExportMenuOpen, setIsExportMenuOpen);
|
|
2821
|
-
const capabilities = artifactCapabilities(packageInfo);
|
|
2822
|
-
const exportTargets = exportTargetsForCapabilities(capabilities);
|
|
2823
|
-
const lastRefresh = formatDate(snapshot?.generatedAt ?? manifest?.generatedAt);
|
|
2824
|
-
const statusLabel = snapshotStatusLabel(snapshot?.status);
|
|
2825
|
-
const dateLabel = lastRefresh;
|
|
2826
|
-
const showStatusLabel = manifest?.surface !== "report" && statusLabel;
|
|
2827
|
-
const showActions = chrome === "full";
|
|
2828
|
-
const showInlineExpand = chrome === "inline" && typeof onRequestFullscreen === "function";
|
|
2829
|
-
function requestRefresh() {
|
|
2830
|
-
void openCodexPrompt(refreshPrompt(manifest, snapshot, packageInfo), packageInfo, onCopyResult);
|
|
2831
|
-
}
|
|
2832
|
-
function requestExport(target) {
|
|
2833
|
-
closeMenu();
|
|
2834
|
-
void openCodexPrompt(exportPrompt(target, manifest, snapshot, packageInfo), packageInfo, onCopyResult);
|
|
2835
|
-
}
|
|
2836
|
-
return (<div className="analytics-top-bar" aria-label={`${appSurfaceLabel(manifest)} actions`}>
|
|
2837
|
-
<div className="analytics-top-bar-title">
|
|
2838
|
-
<EditablePageTitle ariaLabel={`Edit ${appSurfaceLabel(manifest)} title`} isEditMode={isEditMode} onChange={onTitleChange} onRequestEditMode={onEdit} placeholder={composePageTitle(manifest)} readOnly={!showActions || !capabilities.canEdit} title={title}/>
|
|
2839
|
-
</div>
|
|
2840
|
-
{showInlineExpand ? (<div className="analytics-top-bar-actions">
|
|
2841
|
-
<button aria-label={`Expand ${appSurfaceLabel(manifest)} fullscreen`} className="top-bar-button" onClick={onRequestFullscreen} title={`Expand ${appSurfaceLabel(manifest)} fullscreen`} type="button">
|
|
2842
|
-
<Expand aria-hidden="true" size={14} strokeWidth={2}/>
|
|
2843
|
-
<span>Expand</span>
|
|
2844
|
-
</button>
|
|
2845
|
-
</div>) : showActions ? (<div className="analytics-top-bar-actions">
|
|
2846
|
-
<AnalyticsTopBarFreshness dateLabel={dateLabel} onRefresh={requestRefresh} status={snapshot?.status} statusLabel={showStatusLabel ? statusLabel : null}/>
|
|
2847
|
-
{isEditMode && capabilities.canEdit ? (<>
|
|
2848
|
-
<button className="top-bar-button" onClick={onCancelEdit} type="button">
|
|
2849
|
-
<span>Cancel</span>
|
|
2850
|
-
</button>
|
|
2851
|
-
<button className="top-bar-button top-bar-button-primary" onClick={onSaveEdit} type="button">
|
|
2852
|
-
<span>Save changes</span>
|
|
2853
|
-
</button>
|
|
2854
|
-
</>) : (<>
|
|
2855
|
-
{capabilities.canEdit ? (
|
|
2856
|
-
<button className="top-bar-button top-bar-edit-button" onClick={onEdit} type="button">
|
|
2857
|
-
<Pencil aria-hidden="true" size={14} strokeWidth={2}/>
|
|
2858
|
-
<span>Edit</span>
|
|
2859
|
-
</button>
|
|
2860
|
-
): null}
|
|
2861
|
-
{exportTargets.length ? (
|
|
2862
|
-
<div className="export-menu">
|
|
2863
|
-
<button ref={menuButtonRef} aria-expanded={isExportMenuOpen} aria-haspopup="menu" className="top-bar-button" onClick={toggleMenu} onKeyDown={handleMenuButtonKeyDown} type="button">
|
|
2864
|
-
<span>Export</span>
|
|
2865
|
-
<ChevronDown aria-hidden="true" size={14} strokeWidth={2}/>
|
|
2866
|
-
</button>
|
|
2867
|
-
{shouldRenderMenu ? (<div ref={menuRef} className={`export-menu-list menu-surface ${menuMotionClass}`} onKeyDown={handleMenuKeyDown} role="menu" style={fixedMenuStyle}>
|
|
2868
|
-
{exportTargets.map((target) => (<button className="export-menu-item" key={target} onClick={() => requestExport(target)} role="menuitem" type="button">
|
|
2869
|
-
{EXPORT_TARGET_ICONS[target]}
|
|
2870
|
-
{EXPORT_TARGET_LABELS[target]}
|
|
2871
|
-
</button>))}
|
|
2872
|
-
</div>) : null}
|
|
2873
|
-
</div>
|
|
2874
|
-
): null}
|
|
2875
|
-
</>)}
|
|
2876
|
-
</div>) : null}
|
|
2877
|
-
</div>);
|
|
2878
|
-
}
|
|
2879
|
-
export default function App({ displayMode = "fullscreen", onRequestFullscreen } = {}) {
|
|
2880
|
-
const [manifest, setManifest] = useState(null);
|
|
2881
|
-
const [snapshot, setSnapshot] = useState(null);
|
|
2882
|
-
const [packageInfo, setPackageInfo] = useState(null);
|
|
2883
|
-
const [error, setError] = useState(null);
|
|
2884
|
-
const [selectedFilters, setSelectedFilters] = useState({});
|
|
2885
|
-
const [tableColumnWidths, setTableColumnWidths] = useState({});
|
|
2886
|
-
const [pageTitle, setPageTitle] = useState("");
|
|
2887
|
-
const [chartTextOverrides, setChartTextOverrides] = useState({});
|
|
2888
|
-
const [chartSpecOverrides, setChartSpecOverrides] = useState({});
|
|
2889
|
-
const [chartTypeOverrides, setChartTypeOverrides] = useState({});
|
|
2890
|
-
const [tableTextOverrides, setTableTextOverrides] = useState({});
|
|
2891
|
-
const [blockTextOverrides, setBlockTextOverrides] = useState({});
|
|
2892
|
-
const [deletedReportBlockIds, setDeletedReportBlockIds] = useState([]);
|
|
2893
|
-
const [openMenuChartId, setOpenMenuChartId] = useState(null);
|
|
2894
|
-
const [openMenuTableId, setOpenMenuTableId] = useState(null);
|
|
2895
|
-
const [openMenuBlockId, setOpenMenuBlockId] = useState(null);
|
|
2896
|
-
const [chartModal, setChartModal] = useState(null);
|
|
2897
|
-
const [tableModal, setTableModal] = useState(null);
|
|
2898
|
-
const [copyMessage, setCopyMessage] = useState(null);
|
|
2899
|
-
const [isEditMode, setIsEditMode] = useState(false);
|
|
2900
|
-
const [layoutResetKey, setLayoutResetKey] = useState(0);
|
|
2901
|
-
const editSnapshotRef = useRef(null);
|
|
2902
|
-
const layoutDraftsRef = useRef({ dashboard: null, report: null });
|
|
2903
|
-
useEffect(() => {
|
|
2904
|
-
async function load() {
|
|
2905
|
-
try {
|
|
2906
|
-
const [nextManifest, nextSnapshot, nextPackageInfo] = await Promise.all([
|
|
2907
|
-
fetchJson("/api/manifest"),
|
|
2908
|
-
fetchJson("/api/snapshot"),
|
|
2909
|
-
fetchOptionalJson("/api/package")
|
|
2910
|
-
]);
|
|
2911
|
-
setManifest(normalizeManifest(nextManifest));
|
|
2912
|
-
setSnapshot(normalizeSnapshot(nextSnapshot));
|
|
2913
|
-
setPackageInfo(nextPackageInfo);
|
|
2914
|
-
}
|
|
2915
|
-
catch (loadError) {
|
|
2916
|
-
setError(loadError instanceof Error ? loadError.message : "Failed to load report");
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
void load();
|
|
2920
|
-
}, []);
|
|
2921
|
-
const filters = manifest?.filters ?? EMPTY_FILTERS;
|
|
2922
|
-
const cards = manifest?.cards ?? EMPTY_CARDS;
|
|
2923
|
-
const charts = manifest?.charts ?? EMPTY_CHARTS;
|
|
2924
|
-
const tables = manifest?.tables ?? EMPTY_TABLES;
|
|
2925
|
-
const reportBlocks = manifest?.blocks ?? EMPTY_REPORT_BLOCKS;
|
|
2926
|
-
const globalFilters = useMemo(() => getGlobalFilters(filters, cards, charts, tables), [cards, charts, filters, tables]);
|
|
2927
|
-
const accessIssues = snapshot?.accessIssues ?? EMPTY_ACCESS_ISSUES;
|
|
2928
|
-
const storageKey = contentLayoutKey(manifest);
|
|
2929
|
-
const chartTextStorageKey = chartTextKey(manifest);
|
|
2930
|
-
const chartSpecStorageKey = chartSpecKey(manifest);
|
|
2931
|
-
const chartTypeStorageKey = chartTypeKey(manifest);
|
|
2932
|
-
const pageTitleTextStorageKey = pageTitleTextKey(manifest);
|
|
2933
|
-
const tableTextStorageKey = tableTextKey(manifest);
|
|
2934
|
-
const blockTextStorageKey = blockTextKey(manifest);
|
|
2935
|
-
const deletedReportBlockStorageKey = deletedReportBlocksKey(manifest);
|
|
2936
|
-
const tableColumnWidthStorageKey = tableColumnWidthKey(manifest);
|
|
2937
|
-
const reportStorageKey = reportContentLayoutKey(manifest);
|
|
2938
|
-
const isReport = manifest?.surface === "report";
|
|
2939
|
-
const activeSurfaceFilters = isReport ? filters : globalFilters;
|
|
2940
|
-
const capabilities = useMemo(() => artifactCapabilities(packageInfo), [packageInfo]);
|
|
2941
|
-
const activeEditMode = capabilities.canEdit && isEditMode;
|
|
2942
|
-
const requestEditMode = capabilities.canEdit ? beginEditMode : undefined;
|
|
2943
|
-
useEffect(() => {
|
|
2944
|
-
document.title = pageTitle.trim() || composePageTitle(manifest);
|
|
2945
|
-
}, [manifest, pageTitle]);
|
|
2946
|
-
useEffect(() => {
|
|
2947
|
-
if (!capabilities.canEdit && isEditMode) {
|
|
2948
|
-
setIsEditMode(false);
|
|
2949
|
-
}
|
|
2950
|
-
}, [capabilities.canEdit, isEditMode]);
|
|
2951
|
-
const cardsById = useMemo(() => new Map(cards.map((card) => [card.id, card])), [cards]);
|
|
2952
|
-
const chartsById = useMemo(() => new Map(charts.map((chart) => [chart.id, chart])), [charts]);
|
|
2953
|
-
const tablesById = useMemo(() => new Map(tables.map((table) => [table.id, table])), [tables]);
|
|
2954
|
-
const reportGridBlocks = reportBlocks;
|
|
2955
|
-
const visibleReportGridBlocks = useMemo(() => {
|
|
2956
|
-
if (!deletedReportBlockIds.length)
|
|
2957
|
-
return reportGridBlocks;
|
|
2958
|
-
const deletedIds = new Set(deletedReportBlockIds);
|
|
2959
|
-
return reportGridBlocks.filter((block) => !deletedIds.has(block.id));
|
|
2960
|
-
}, [deletedReportBlockIds, reportGridBlocks]);
|
|
2961
|
-
useEffect(() => {
|
|
2962
|
-
if (!reportGridBlocks.length || !deletedReportBlockStorageKey) {
|
|
2963
|
-
setDeletedReportBlockIds([]);
|
|
2964
|
-
return;
|
|
2965
|
-
}
|
|
2966
|
-
const stored = window.localStorage.getItem(deletedReportBlockStorageKey);
|
|
2967
|
-
if (!stored) {
|
|
2968
|
-
setDeletedReportBlockIds([]);
|
|
2969
|
-
return;
|
|
2970
|
-
}
|
|
2971
|
-
try {
|
|
2972
|
-
const parsed = JSON.parse(stored);
|
|
2973
|
-
const knownIds = new Set(reportGridBlocks.map((block) => block.id));
|
|
2974
|
-
const nextIds = Array.isArray(parsed)
|
|
2975
|
-
? parsed.filter((id) => typeof id === "string" && knownIds.has(id))
|
|
2976
|
-
: [];
|
|
2977
|
-
setDeletedReportBlockIds(nextIds);
|
|
2978
|
-
}
|
|
2979
|
-
catch {
|
|
2980
|
-
setDeletedReportBlockIds([]);
|
|
2981
|
-
}
|
|
2982
|
-
}, [deletedReportBlockStorageKey, reportGridBlocks]);
|
|
2983
|
-
useEffect(() => {
|
|
2984
|
-
if (!tables.length || !tableColumnWidthStorageKey) {
|
|
2985
|
-
setTableColumnWidths({});
|
|
2986
|
-
return;
|
|
2987
|
-
}
|
|
2988
|
-
const stored = window.localStorage.getItem(tableColumnWidthStorageKey);
|
|
2989
|
-
if (!stored) {
|
|
2990
|
-
setTableColumnWidths({});
|
|
2991
|
-
return;
|
|
2992
|
-
}
|
|
2993
|
-
try {
|
|
2994
|
-
const parsed = JSON.parse(stored);
|
|
2995
|
-
const tableFields = new Map(tables.map((table) => [table.id, new Set(table.columns.map((column) => column.field))]));
|
|
2996
|
-
const nextState = {};
|
|
2997
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2998
|
-
for (const [tableId, rawWidths] of Object.entries(parsed)) {
|
|
2999
|
-
const fields = tableFields.get(tableId);
|
|
3000
|
-
if (!fields || !rawWidths || typeof rawWidths !== "object" || Array.isArray(rawWidths)) {
|
|
3001
|
-
continue;
|
|
3002
|
-
}
|
|
3003
|
-
const nextWidths = {};
|
|
3004
|
-
for (const [field, rawWidth] of Object.entries(rawWidths)) {
|
|
3005
|
-
const width = typeof rawWidth === "number" ? rawWidth : Number(rawWidth);
|
|
3006
|
-
if (fields.has(field) && Number.isFinite(width)) {
|
|
3007
|
-
nextWidths[field] = clamp(Math.round(width), TABLE_COLUMN_MIN_WIDTH, TABLE_COLUMN_MAX_WIDTH);
|
|
3008
|
-
}
|
|
3009
|
-
}
|
|
3010
|
-
if (Object.keys(nextWidths).length) {
|
|
3011
|
-
nextState[tableId] = nextWidths;
|
|
3012
|
-
}
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
setTableColumnWidths(nextState);
|
|
3016
|
-
}
|
|
3017
|
-
catch {
|
|
3018
|
-
setTableColumnWidths({});
|
|
3019
|
-
}
|
|
3020
|
-
}, [tableColumnWidthStorageKey, tables]);
|
|
3021
|
-
useEffect(() => {
|
|
3022
|
-
const fallbackTitle = composePageTitle(manifest);
|
|
3023
|
-
if (!pageTitleTextStorageKey) {
|
|
3024
|
-
setPageTitle(fallbackTitle);
|
|
3025
|
-
return;
|
|
3026
|
-
}
|
|
3027
|
-
const storedTitle = window.localStorage.getItem(pageTitleTextStorageKey);
|
|
3028
|
-
setPageTitle(storedTitle ?? fallbackTitle);
|
|
3029
|
-
}, [manifest, pageTitleTextStorageKey]);
|
|
3030
|
-
useEffect(() => {
|
|
3031
|
-
if (!charts.length || !chartTextStorageKey)
|
|
3032
|
-
return;
|
|
3033
|
-
const stored = window.localStorage.getItem(chartTextStorageKey);
|
|
3034
|
-
if (!stored) {
|
|
3035
|
-
setChartTextOverrides({});
|
|
3036
|
-
return;
|
|
3037
|
-
}
|
|
3038
|
-
try {
|
|
3039
|
-
const parsed = JSON.parse(stored);
|
|
3040
|
-
const knownIds = new Set(charts.map((chart) => chart.id));
|
|
3041
|
-
const nextOverrides = {};
|
|
3042
|
-
for (const [chartId, override] of Object.entries(parsed)) {
|
|
3043
|
-
if (knownIds.has(chartId)
|
|
3044
|
-
&& override
|
|
3045
|
-
&& typeof override === "object"
|
|
3046
|
-
&& (typeof override.headerMarkdown === "string"
|
|
3047
|
-
|| typeof override.title === "string"
|
|
3048
|
-
|| typeof override.subtitle === "string")) {
|
|
3049
|
-
nextOverrides[chartId] = {
|
|
3050
|
-
...(typeof override.headerMarkdown === "string" ? { headerMarkdown: override.headerMarkdown } : {}),
|
|
3051
|
-
...(typeof override.title === "string" ? { title: override.title } : {}),
|
|
3052
|
-
...(typeof override.subtitle === "string" ? { subtitle: override.subtitle } : {})
|
|
3053
|
-
};
|
|
3054
|
-
}
|
|
3055
|
-
}
|
|
3056
|
-
setChartTextOverrides(nextOverrides);
|
|
3057
|
-
}
|
|
3058
|
-
catch {
|
|
3059
|
-
setChartTextOverrides({});
|
|
3060
|
-
}
|
|
3061
|
-
}, [chartTextStorageKey, charts]);
|
|
3062
|
-
useEffect(() => {
|
|
3063
|
-
if (!charts.length || !chartSpecStorageKey) {
|
|
3064
|
-
setChartSpecOverrides({});
|
|
3065
|
-
return;
|
|
3066
|
-
}
|
|
3067
|
-
const stored = window.localStorage.getItem(chartSpecStorageKey);
|
|
3068
|
-
if (!stored) {
|
|
3069
|
-
setChartSpecOverrides({});
|
|
3070
|
-
return;
|
|
3071
|
-
}
|
|
3072
|
-
try {
|
|
3073
|
-
const parsed = JSON.parse(stored);
|
|
3074
|
-
const chartsByIdForStorage = new Map(charts.map((chart) => [chart.id, chart]));
|
|
3075
|
-
const nextOverrides = {};
|
|
3076
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3077
|
-
for (const [chartId, override] of Object.entries(parsed)) {
|
|
3078
|
-
const chart = chartsByIdForStorage.get(chartId);
|
|
3079
|
-
if (!chart || !override || typeof override !== "object" || Array.isArray(override)) {
|
|
3080
|
-
continue;
|
|
3081
|
-
}
|
|
3082
|
-
const sanitized = {};
|
|
3083
|
-
if (sharedIsChartType(override.type)) {
|
|
3084
|
-
sanitized.type = override.type;
|
|
3085
|
-
}
|
|
3086
|
-
const chartFields = new Set(chartUsedFields(chart));
|
|
3087
|
-
if (override.encodings && typeof override.encodings === "object" && !Array.isArray(override.encodings)) {
|
|
3088
|
-
const sizeEncoding = override.encodings.size;
|
|
3089
|
-
if (sizeEncoding && typeof sizeEncoding === "object" && !Array.isArray(sizeEncoding) && typeof sizeEncoding.field === "string" && chartFields.has(sizeEncoding.field)) {
|
|
3090
|
-
sanitized.encodings = { ...chart.encodings, size: { ...sizeEncoding, field: sizeEncoding.field } };
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
if (override.settings && typeof override.settings === "object" && !Array.isArray(override.settings)) {
|
|
3094
|
-
sanitized.settings = {
|
|
3095
|
-
...(override.settings.orientation === "horizontal" || override.settings.orientation === "vertical" ? { orientation: override.settings.orientation } : {}),
|
|
3096
|
-
...(["grouped", "stacked", "stacked100"].includes(override.settings.groupMode) ? { groupMode: override.settings.groupMode } : {}),
|
|
3097
|
-
};
|
|
3098
|
-
if (!Object.keys(sanitized.settings).length) {
|
|
3099
|
-
delete sanitized.settings;
|
|
3100
|
-
}
|
|
3101
|
-
}
|
|
3102
|
-
if (Object.keys(sanitized).length) {
|
|
3103
|
-
nextOverrides[chartId] = sanitized;
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
setChartSpecOverrides(nextOverrides);
|
|
3108
|
-
}
|
|
3109
|
-
catch {
|
|
3110
|
-
setChartSpecOverrides({});
|
|
3111
|
-
}
|
|
3112
|
-
}, [chartSpecStorageKey, charts]);
|
|
3113
|
-
useEffect(() => {
|
|
3114
|
-
if (!charts.length || !chartTypeStorageKey) {
|
|
3115
|
-
setChartTypeOverrides({});
|
|
3116
|
-
return;
|
|
3117
|
-
}
|
|
3118
|
-
const stored = window.localStorage.getItem(chartTypeStorageKey);
|
|
3119
|
-
if (!stored) {
|
|
3120
|
-
setChartTypeOverrides({});
|
|
3121
|
-
return;
|
|
3122
|
-
}
|
|
3123
|
-
try {
|
|
3124
|
-
const parsed = JSON.parse(stored);
|
|
3125
|
-
const knownIds = new Set(charts.map((chart) => chart.id));
|
|
3126
|
-
const nextOverrides = {};
|
|
3127
|
-
for (const [chartId, chartType] of Object.entries(parsed)) {
|
|
3128
|
-
if (knownIds.has(chartId) && isChartType(chartType)) {
|
|
3129
|
-
nextOverrides[chartId] = chartType;
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
setChartTypeOverrides(nextOverrides);
|
|
3133
|
-
}
|
|
3134
|
-
catch {
|
|
3135
|
-
setChartTypeOverrides({});
|
|
3136
|
-
}
|
|
3137
|
-
}, [chartTypeStorageKey, charts]);
|
|
3138
|
-
useEffect(() => {
|
|
3139
|
-
if (!tables.length || !tableTextStorageKey)
|
|
3140
|
-
return;
|
|
3141
|
-
const stored = window.localStorage.getItem(tableTextStorageKey);
|
|
3142
|
-
if (!stored) {
|
|
3143
|
-
setTableTextOverrides({});
|
|
3144
|
-
return;
|
|
3145
|
-
}
|
|
3146
|
-
try {
|
|
3147
|
-
const parsed = JSON.parse(stored);
|
|
3148
|
-
const knownIds = new Set(tables.map((table) => table.id));
|
|
3149
|
-
const nextOverrides = {};
|
|
3150
|
-
for (const [tableId, override] of Object.entries(parsed)) {
|
|
3151
|
-
if (knownIds.has(tableId)
|
|
3152
|
-
&& override
|
|
3153
|
-
&& typeof override === "object"
|
|
3154
|
-
&& typeof override.headerMarkdown === "string") {
|
|
3155
|
-
nextOverrides[tableId] = { headerMarkdown: override.headerMarkdown };
|
|
3156
|
-
}
|
|
3157
|
-
}
|
|
3158
|
-
setTableTextOverrides(nextOverrides);
|
|
3159
|
-
}
|
|
3160
|
-
catch {
|
|
3161
|
-
setTableTextOverrides({});
|
|
3162
|
-
}
|
|
3163
|
-
}, [tableTextStorageKey, tables]);
|
|
3164
|
-
useEffect(() => {
|
|
3165
|
-
if (!reportGridBlocks.length || !blockTextStorageKey)
|
|
3166
|
-
return;
|
|
3167
|
-
const stored = window.localStorage.getItem(blockTextStorageKey);
|
|
3168
|
-
if (!stored) {
|
|
3169
|
-
setBlockTextOverrides({});
|
|
3170
|
-
return;
|
|
3171
|
-
}
|
|
3172
|
-
try {
|
|
3173
|
-
const parsed = JSON.parse(stored);
|
|
3174
|
-
const knownIds = new Set(reportGridBlocks.map((block) => block.id));
|
|
3175
|
-
const nextOverrides = {};
|
|
3176
|
-
for (const [blockId, override] of Object.entries(parsed)) {
|
|
3177
|
-
if (knownIds.has(blockId) && override && typeof override === "object") {
|
|
3178
|
-
const nextOverride = {
|
|
3179
|
-
...(visibleString(override.bodyMarkdown) ? { bodyMarkdown: override.bodyMarkdown } : {}),
|
|
3180
|
-
...(visibleString(override.html) ? { html: override.html } : {})
|
|
3181
|
-
};
|
|
3182
|
-
if (Object.keys(nextOverride).length)
|
|
3183
|
-
nextOverrides[blockId] = nextOverride;
|
|
3184
|
-
}
|
|
3185
|
-
}
|
|
3186
|
-
setBlockTextOverrides(nextOverrides);
|
|
3187
|
-
}
|
|
3188
|
-
catch {
|
|
3189
|
-
setBlockTextOverrides({});
|
|
3190
|
-
}
|
|
3191
|
-
}, [blockTextStorageKey, reportGridBlocks]);
|
|
3192
|
-
const filterDefaults = useMemo(() => {
|
|
3193
|
-
return Object.fromEntries(activeSurfaceFilters.map((filter) => [filter.id, filter.defaultValue ?? "all"]));
|
|
3194
|
-
}, [activeSurfaceFilters]);
|
|
3195
|
-
useEffect(() => {
|
|
3196
|
-
setSelectedFilters((current) => ({ ...filterDefaults, ...current }));
|
|
3197
|
-
}, [filterDefaults]);
|
|
3198
|
-
useEffect(() => {
|
|
3199
|
-
if (!copyMessage)
|
|
3200
|
-
return;
|
|
3201
|
-
const timeout = window.setTimeout(() => setCopyMessage(null), 2600);
|
|
3202
|
-
return () => window.clearTimeout(timeout);
|
|
3203
|
-
}, [copyMessage]);
|
|
3204
|
-
function persistEditableState() {
|
|
3205
|
-
if (pageTitleTextStorageKey) {
|
|
3206
|
-
window.localStorage.setItem(pageTitleTextStorageKey, pageTitle);
|
|
3207
|
-
}
|
|
3208
|
-
if (chartTextStorageKey) {
|
|
3209
|
-
window.localStorage.setItem(chartTextStorageKey, JSON.stringify(chartTextOverrides));
|
|
3210
|
-
}
|
|
3211
|
-
if (chartSpecStorageKey) {
|
|
3212
|
-
window.localStorage.setItem(chartSpecStorageKey, JSON.stringify(chartSpecOverrides));
|
|
3213
|
-
}
|
|
3214
|
-
if (chartTypeStorageKey) {
|
|
3215
|
-
window.localStorage.setItem(chartTypeStorageKey, JSON.stringify(chartTypeOverrides));
|
|
3216
|
-
}
|
|
3217
|
-
if (tableTextStorageKey) {
|
|
3218
|
-
window.localStorage.setItem(tableTextStorageKey, JSON.stringify(tableTextOverrides));
|
|
3219
|
-
}
|
|
3220
|
-
if (blockTextStorageKey) {
|
|
3221
|
-
window.localStorage.setItem(blockTextStorageKey, JSON.stringify(blockTextOverrides));
|
|
3222
|
-
}
|
|
3223
|
-
if (deletedReportBlockStorageKey) {
|
|
3224
|
-
window.localStorage.setItem(deletedReportBlockStorageKey, JSON.stringify(deletedReportBlockIds));
|
|
3225
|
-
}
|
|
3226
|
-
if (tableColumnWidthStorageKey) {
|
|
3227
|
-
window.localStorage.setItem(tableColumnWidthStorageKey, JSON.stringify(tableColumnWidths));
|
|
3228
|
-
}
|
|
3229
|
-
if (storageKey && layoutDraftsRef.current.dashboard) {
|
|
3230
|
-
window.localStorage.setItem(storageKey, JSON.stringify(layoutDraftsRef.current.dashboard));
|
|
3231
|
-
}
|
|
3232
|
-
if (reportStorageKey && layoutDraftsRef.current.report) {
|
|
3233
|
-
window.localStorage.setItem(reportStorageKey, JSON.stringify(layoutDraftsRef.current.report));
|
|
3234
|
-
}
|
|
3235
|
-
}
|
|
3236
|
-
function closeInlineMenus() {
|
|
3237
|
-
setOpenMenuBlockId(null);
|
|
3238
|
-
setOpenMenuChartId(null);
|
|
3239
|
-
setOpenMenuTableId(null);
|
|
3240
|
-
}
|
|
3241
|
-
function beginEditMode() {
|
|
3242
|
-
if (isEditMode)
|
|
3243
|
-
return;
|
|
3244
|
-
editSnapshotRef.current = {
|
|
3245
|
-
blockTextOverrides: cloneSerializable(blockTextOverrides),
|
|
3246
|
-
chartSpecOverrides: cloneSerializable(chartSpecOverrides),
|
|
3247
|
-
chartTextOverrides: cloneSerializable(chartTextOverrides),
|
|
3248
|
-
chartTypeOverrides: cloneSerializable(chartTypeOverrides),
|
|
3249
|
-
deletedReportBlockIds: [...deletedReportBlockIds],
|
|
3250
|
-
pageTitle,
|
|
3251
|
-
tableColumnWidths: cloneSerializable(tableColumnWidths),
|
|
3252
|
-
tableTextOverrides: cloneSerializable(tableTextOverrides)
|
|
3253
|
-
};
|
|
3254
|
-
layoutDraftsRef.current = { dashboard: null, report: null };
|
|
3255
|
-
closeInlineMenus();
|
|
3256
|
-
setIsEditMode(true);
|
|
3257
|
-
}
|
|
3258
|
-
function cancelEditMode() {
|
|
3259
|
-
const snapshotState = editSnapshotRef.current;
|
|
3260
|
-
if (snapshotState) {
|
|
3261
|
-
setBlockTextOverrides(cloneSerializable(snapshotState.blockTextOverrides));
|
|
3262
|
-
setChartSpecOverrides(cloneSerializable(snapshotState.chartSpecOverrides ?? {}));
|
|
3263
|
-
setChartTextOverrides(cloneSerializable(snapshotState.chartTextOverrides));
|
|
3264
|
-
setChartTypeOverrides(cloneSerializable(snapshotState.chartTypeOverrides));
|
|
3265
|
-
setDeletedReportBlockIds([...snapshotState.deletedReportBlockIds]);
|
|
3266
|
-
setPageTitle(snapshotState.pageTitle);
|
|
3267
|
-
setTableColumnWidths(cloneSerializable(snapshotState.tableColumnWidths));
|
|
3268
|
-
setTableTextOverrides(cloneSerializable(snapshotState.tableTextOverrides));
|
|
3269
|
-
}
|
|
3270
|
-
editSnapshotRef.current = null;
|
|
3271
|
-
layoutDraftsRef.current = { dashboard: null, report: null };
|
|
3272
|
-
closeInlineMenus();
|
|
3273
|
-
setIsEditMode(false);
|
|
3274
|
-
setLayoutResetKey((current) => current + 1);
|
|
3275
|
-
}
|
|
3276
|
-
function saveEditMode() {
|
|
3277
|
-
persistEditableState();
|
|
3278
|
-
editSnapshotRef.current = null;
|
|
3279
|
-
layoutDraftsRef.current = { dashboard: null, report: null };
|
|
3280
|
-
closeInlineMenus();
|
|
3281
|
-
setIsEditMode(false);
|
|
3282
|
-
}
|
|
3283
|
-
function recordDashboardLayoutDraft(nextItems) {
|
|
3284
|
-
layoutDraftsRef.current.dashboard = nextItems;
|
|
3285
|
-
}
|
|
3286
|
-
function recordReportLayoutDraft(nextItems) {
|
|
3287
|
-
layoutDraftsRef.current.report = nextItems;
|
|
3288
|
-
}
|
|
3289
|
-
function updatePageTitle(nextTitle) {
|
|
3290
|
-
setPageTitle(nextTitle);
|
|
3291
|
-
if (!isEditMode && pageTitleTextStorageKey) {
|
|
3292
|
-
window.localStorage.setItem(pageTitleTextStorageKey, nextTitle);
|
|
3293
|
-
}
|
|
3294
|
-
}
|
|
3295
|
-
function updateChartText(chartId, nextText) {
|
|
3296
|
-
setChartTextOverrides((current) => {
|
|
3297
|
-
const merged = {
|
|
3298
|
-
...current,
|
|
3299
|
-
[chartId]: {
|
|
3300
|
-
...current[chartId],
|
|
3301
|
-
...nextText
|
|
3302
|
-
}
|
|
3303
|
-
};
|
|
3304
|
-
if (!isEditMode && chartTextStorageKey) {
|
|
3305
|
-
window.localStorage.setItem(chartTextStorageKey, JSON.stringify(merged));
|
|
3306
|
-
}
|
|
3307
|
-
return merged;
|
|
3308
|
-
});
|
|
3309
|
-
}
|
|
3310
|
-
function updateChartType(chartId, nextType) {
|
|
3311
|
-
setChartTypeOverrides((current) => {
|
|
3312
|
-
const originalType = charts.find((chart) => chart.id === chartId)?.type;
|
|
3313
|
-
const merged = { ...current };
|
|
3314
|
-
if (!originalType || nextType === originalType) {
|
|
3315
|
-
delete merged[chartId];
|
|
3316
|
-
}
|
|
3317
|
-
else {
|
|
3318
|
-
merged[chartId] = nextType;
|
|
3319
|
-
}
|
|
3320
|
-
if (!isEditMode && chartTypeStorageKey) {
|
|
3321
|
-
window.localStorage.setItem(chartTypeStorageKey, JSON.stringify(merged));
|
|
3322
|
-
}
|
|
3323
|
-
return merged;
|
|
3324
|
-
});
|
|
3325
|
-
}
|
|
3326
|
-
function updateChartSpec(chartId, widgetSpec) {
|
|
3327
|
-
const chart = charts.find((candidate) => candidate.id === chartId);
|
|
3328
|
-
if (!chart)
|
|
3329
|
-
return;
|
|
3330
|
-
if (!widgetSpec) {
|
|
3331
|
-
setChartSpecOverrides((current) => {
|
|
3332
|
-
const merged = { ...current };
|
|
3333
|
-
delete merged[chartId];
|
|
3334
|
-
if (!isEditMode && chartSpecStorageKey) {
|
|
3335
|
-
window.localStorage.setItem(chartSpecStorageKey, JSON.stringify(merged));
|
|
3336
|
-
}
|
|
3337
|
-
return merged;
|
|
3338
|
-
});
|
|
3339
|
-
setChartTypeOverrides((current) => {
|
|
3340
|
-
const merged = { ...current };
|
|
3341
|
-
delete merged[chartId];
|
|
3342
|
-
if (!isEditMode && chartTypeStorageKey) {
|
|
3343
|
-
window.localStorage.setItem(chartTypeStorageKey, JSON.stringify(merged));
|
|
3344
|
-
}
|
|
3345
|
-
return merged;
|
|
3346
|
-
});
|
|
3347
|
-
return;
|
|
3348
|
-
}
|
|
3349
|
-
const nextOverride = chartSpecOverrideFromWidgetSpec(chart, widgetSpec);
|
|
3350
|
-
const nextType = sharedIsChartType(nextOverride.type) ? nextOverride.type : chart.type;
|
|
3351
|
-
setChartSpecOverrides((current) => {
|
|
3352
|
-
const merged = { ...current };
|
|
3353
|
-
if (Object.keys(nextOverride).length) {
|
|
3354
|
-
merged[chartId] = nextOverride;
|
|
3355
|
-
}
|
|
3356
|
-
else {
|
|
3357
|
-
delete merged[chartId];
|
|
3358
|
-
}
|
|
3359
|
-
if (!isEditMode && chartSpecStorageKey) {
|
|
3360
|
-
window.localStorage.setItem(chartSpecStorageKey, JSON.stringify(merged));
|
|
3361
|
-
}
|
|
3362
|
-
return merged;
|
|
3363
|
-
});
|
|
3364
|
-
setChartTypeOverrides((current) => {
|
|
3365
|
-
const merged = { ...current };
|
|
3366
|
-
if (nextType === chart.type) {
|
|
3367
|
-
delete merged[chartId];
|
|
3368
|
-
}
|
|
3369
|
-
else {
|
|
3370
|
-
merged[chartId] = nextType;
|
|
3371
|
-
}
|
|
3372
|
-
if (!isEditMode && chartTypeStorageKey) {
|
|
3373
|
-
window.localStorage.setItem(chartTypeStorageKey, JSON.stringify(merged));
|
|
3374
|
-
}
|
|
3375
|
-
return merged;
|
|
3376
|
-
});
|
|
3377
|
-
}
|
|
3378
|
-
function updateTableColumnWidths(tableId, nextWidths, options = {}) {
|
|
3379
|
-
setTableColumnWidths((current) => {
|
|
3380
|
-
const tableSpec = tablesById.get(tableId);
|
|
3381
|
-
if (!tableSpec)
|
|
3382
|
-
return current;
|
|
3383
|
-
const validFields = new Set(tableSpec.columns.map((column) => column.field));
|
|
3384
|
-
const sanitizedWidths = {};
|
|
3385
|
-
for (const [field, rawWidth] of Object.entries(nextWidths)) {
|
|
3386
|
-
const width = typeof rawWidth === "number" ? rawWidth : Number(rawWidth);
|
|
3387
|
-
if (validFields.has(field) && Number.isFinite(width)) {
|
|
3388
|
-
sanitizedWidths[field] = clamp(Math.round(width), TABLE_COLUMN_MIN_WIDTH, TABLE_COLUMN_MAX_WIDTH);
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
|
-
const merged = { ...current };
|
|
3392
|
-
if (Object.keys(sanitizedWidths).length) {
|
|
3393
|
-
merged[tableId] = sanitizedWidths;
|
|
3394
|
-
}
|
|
3395
|
-
else {
|
|
3396
|
-
delete merged[tableId];
|
|
3397
|
-
}
|
|
3398
|
-
if (!isEditMode && options.persist !== false && tableColumnWidthStorageKey) {
|
|
3399
|
-
window.localStorage.setItem(tableColumnWidthStorageKey, JSON.stringify(merged));
|
|
3400
|
-
}
|
|
3401
|
-
return merged;
|
|
3402
|
-
});
|
|
3403
|
-
}
|
|
3404
|
-
function updateTableText(tableId, nextText) {
|
|
3405
|
-
setTableTextOverrides((current) => {
|
|
3406
|
-
const merged = {
|
|
3407
|
-
...current,
|
|
3408
|
-
[tableId]: {
|
|
3409
|
-
...current[tableId],
|
|
3410
|
-
...nextText
|
|
3411
|
-
}
|
|
3412
|
-
};
|
|
3413
|
-
if (!isEditMode && tableTextStorageKey) {
|
|
3414
|
-
window.localStorage.setItem(tableTextStorageKey, JSON.stringify(merged));
|
|
3415
|
-
}
|
|
3416
|
-
return merged;
|
|
3417
|
-
});
|
|
3418
|
-
}
|
|
3419
|
-
function updateBlockText(blockId, nextText) {
|
|
3420
|
-
setBlockTextOverrides((current) => {
|
|
3421
|
-
const nextBlockOverride = {
|
|
3422
|
-
...current[blockId],
|
|
3423
|
-
...nextText
|
|
3424
|
-
};
|
|
3425
|
-
if ("bodyMarkdown" in nextText && !visibleString(nextText.bodyMarkdown)) {
|
|
3426
|
-
delete nextBlockOverride.bodyMarkdown;
|
|
3427
|
-
}
|
|
3428
|
-
if ("html" in nextText && !visibleString(nextText.html)) {
|
|
3429
|
-
delete nextBlockOverride.html;
|
|
3430
|
-
}
|
|
3431
|
-
const merged = { ...current };
|
|
3432
|
-
if (Object.keys(nextBlockOverride).length)
|
|
3433
|
-
merged[blockId] = nextBlockOverride;
|
|
3434
|
-
else
|
|
3435
|
-
delete merged[blockId];
|
|
3436
|
-
if (!isEditMode && blockTextStorageKey) {
|
|
3437
|
-
window.localStorage.setItem(blockTextStorageKey, JSON.stringify(merged));
|
|
3438
|
-
}
|
|
3439
|
-
return merged;
|
|
3440
|
-
});
|
|
3441
|
-
}
|
|
3442
|
-
function deleteReportBlocks(blockIds) {
|
|
3443
|
-
const idsToDelete = [...new Set(blockIds)];
|
|
3444
|
-
if (!idsToDelete.length)
|
|
3445
|
-
return;
|
|
3446
|
-
setDeletedReportBlockIds((current) => {
|
|
3447
|
-
const existingIds = new Set(current);
|
|
3448
|
-
const nextIds = [...current];
|
|
3449
|
-
for (const blockId of idsToDelete) {
|
|
3450
|
-
if (!existingIds.has(blockId)) {
|
|
3451
|
-
nextIds.push(blockId);
|
|
3452
|
-
}
|
|
3453
|
-
}
|
|
3454
|
-
if (nextIds.length === current.length)
|
|
3455
|
-
return current;
|
|
3456
|
-
if (!isEditMode && deletedReportBlockStorageKey) {
|
|
3457
|
-
window.localStorage.setItem(deletedReportBlockStorageKey, JSON.stringify(nextIds));
|
|
3458
|
-
}
|
|
3459
|
-
return nextIds;
|
|
3460
|
-
});
|
|
3461
|
-
setOpenMenuBlockId(null);
|
|
3462
|
-
setOpenMenuChartId(null);
|
|
3463
|
-
setOpenMenuTableId(null);
|
|
3464
|
-
}
|
|
3465
|
-
function deleteReportBlock(blockId) {
|
|
3466
|
-
deleteReportBlocks([blockId]);
|
|
3467
|
-
}
|
|
3468
|
-
const dashboardContentBlocks = useMemo(() => {
|
|
3469
|
-
const chartBlocks = charts.map((chart) => ({
|
|
3470
|
-
className: "analytics-layout-item-chart",
|
|
3471
|
-
defaultLayout: dashboardCardLayout(chart.layout),
|
|
3472
|
-
id: `chart:${chart.id}`,
|
|
3473
|
-
render: (layout, { setLayout }) => {
|
|
3474
|
-
const overriddenChart = applyChartSpecOverride(chart, chartSpecOverrides[chart.id]);
|
|
3475
|
-
const chartRows = filterRowsForDataset(getRows(snapshot, overriddenChart.dataset), overriddenChart.dataset, activeSurfaceFilters, selectedFilters, chartUsedFields(overriddenChart));
|
|
3476
|
-
const chartTypeOptions = compatibleChartTypesForArtifactCard(overriddenChart, chartRows);
|
|
3477
|
-
const requestedType = chartTypeOverrides[chart.id] ?? overriddenChart.type;
|
|
3478
|
-
const activeType = chartTypeOptions.some((option) => option.type === requestedType)
|
|
3479
|
-
? requestedType
|
|
3480
|
-
: overriddenChart.type;
|
|
3481
|
-
const displayChart = withChartType(overriddenChart, activeType);
|
|
3482
|
-
const accessIssue = accessIssueForChart(displayChart, accessIssues);
|
|
3483
|
-
return (<VizCard accessIssue={accessIssue} chart={displayChart} chartTypeOptions={chartTypeOptions} isEditMode={activeEditMode} isMenuOpen={openMenuChartId === chart.id} layout={layout} onChartTypeChange={updateChartType} onCopyResult={(message, isError = false) => setCopyMessage({ isError, message })} onRequestEditMode={requestEditMode} onTextChange={updateChartText} onMenuOpenChange={(nextOpen) => {
|
|
3484
|
-
setOpenMenuTableId(null);
|
|
3485
|
-
setOpenMenuChartId(nextOpen ? chart.id : null);
|
|
3486
|
-
}} onModalOpen={setChartModal} textOverride={chartTextOverrides[chart.id]}>
|
|
3487
|
-
<ChartBody accessIssue={accessIssue} chart={displayChart} filters={activeSurfaceFilters} layout={layout} selectedFilters={selectedFilters} snapshot={snapshot}/>
|
|
3488
|
-
</VizCard>);
|
|
3489
|
-
}
|
|
3490
|
-
}));
|
|
3491
|
-
const tableBlocks = tables.map((table) => ({
|
|
3492
|
-
className: "analytics-layout-item-table",
|
|
3493
|
-
defaultLayout: dashboardCardLayout(table.layout ?? "full"),
|
|
3494
|
-
id: `table:${table.id}`,
|
|
3495
|
-
render: (layout) => (<DataTable columnWidths={tableColumnWidths[table.id] ?? {}} filters={activeSurfaceFilters} isEditMode={activeEditMode} isMenuOpen={openMenuTableId === table.id} layout={layout} manifest={manifest} onColumnWidthsChange={updateTableColumnWidths} onMenuOpenChange={(nextOpen) => {
|
|
3496
|
-
setOpenMenuChartId(null);
|
|
3497
|
-
setOpenMenuTableId(nextOpen ? table.id : null);
|
|
3498
|
-
}} onModalOpen={setTableModal} onRequestEditMode={requestEditMode} onTextChange={updateTableText} selectedFilters={selectedFilters} snapshot={snapshot} table={table} textOverride={tableTextOverrides[table.id]}/>)
|
|
3499
|
-
}));
|
|
3500
|
-
const htmlBlocks = visibleReportGridBlocks
|
|
3501
|
-
.filter((block) => block.type === "html")
|
|
3502
|
-
.map((block) => ({
|
|
3503
|
-
className: "analytics-layout-item-html",
|
|
3504
|
-
defaultLayout: dashboardCardLayout(block.layout ?? "full"),
|
|
3505
|
-
id: `html:${block.id}`,
|
|
3506
|
-
render: () => (<ReportHtmlBlock block={block} htmlOverride={blockTextOverrides[block.id]} isEditMode={activeEditMode} isMenuOpen={openMenuBlockId === block.id} onHtmlChange={updateBlockText} onCopyResult={(message, isError = false) => setCopyMessage({ isError, message })} onDeleteBlock={capabilities.canEdit ? () => deleteReportBlock(block.id) : undefined} onMenuOpenChange={(nextOpen) => {
|
|
3507
|
-
setOpenMenuChartId(null);
|
|
3508
|
-
setOpenMenuTableId(null);
|
|
3509
|
-
setOpenMenuBlockId(nextOpen ? block.id : null);
|
|
3510
|
-
}}/>)
|
|
3511
|
-
}));
|
|
3512
|
-
return [...chartBlocks, ...tableBlocks, ...htmlBlocks];
|
|
3513
|
-
}, [
|
|
3514
|
-
accessIssues,
|
|
3515
|
-
activeSurfaceFilters,
|
|
3516
|
-
activeEditMode,
|
|
3517
|
-
capabilities.canEdit,
|
|
3518
|
-
chartTextOverrides,
|
|
3519
|
-
chartSpecOverrides,
|
|
3520
|
-
chartTypeOverrides,
|
|
3521
|
-
charts,
|
|
3522
|
-
manifest,
|
|
3523
|
-
openMenuBlockId,
|
|
3524
|
-
openMenuChartId,
|
|
3525
|
-
openMenuTableId,
|
|
3526
|
-
requestEditMode,
|
|
3527
|
-
selectedFilters,
|
|
3528
|
-
snapshot,
|
|
3529
|
-
tableColumnWidths,
|
|
3530
|
-
tableTextOverrides,
|
|
3531
|
-
tables,
|
|
3532
|
-
visibleReportGridBlocks
|
|
3533
|
-
]);
|
|
3534
|
-
const reportContentBlocks = useMemo(() => {
|
|
3535
|
-
return visibleReportGridBlocks.map((block) => {
|
|
3536
|
-
if (block.type === "metric-strip") {
|
|
3537
|
-
const metricCards = (block.cardIds ?? [])
|
|
3538
|
-
.map((cardId) => cardsById.get(cardId))
|
|
3539
|
-
.filter((card) => Boolean(card));
|
|
3540
|
-
return {
|
|
3541
|
-
className: "report-stack-item report-stack-item-metric-strip",
|
|
3542
|
-
defaultLayout: reportCardLayout(block.layout),
|
|
3543
|
-
id: block.id,
|
|
3544
|
-
render: () => (<ReportMetricStripBlock cards={metricCards} filters={activeSurfaceFilters} id={block.id} isMenuOpen={openMenuBlockId === block.id} onCopyResult={(message, isError = false) => setCopyMessage({ isError, message })} onDeleteBlock={capabilities.canEdit ? () => deleteReportBlock(block.id) : undefined} onMenuOpenChange={(nextOpen) => {
|
|
3545
|
-
setOpenMenuChartId(null);
|
|
3546
|
-
setOpenMenuTableId(null);
|
|
3547
|
-
setOpenMenuBlockId(nextOpen ? block.id : null);
|
|
3548
|
-
}} selectedFilters={selectedFilters} snapshot={snapshot}/>)
|
|
3549
|
-
};
|
|
3550
|
-
}
|
|
3551
|
-
const chart = block.chartId ? chartsById.get(block.chartId) : undefined;
|
|
3552
|
-
const table = block.tableId ? tablesById.get(block.tableId) : undefined;
|
|
3553
|
-
return {
|
|
3554
|
-
className: `report-stack-item report-stack-item-${block.type}`,
|
|
3555
|
-
defaultLayout: reportCardLayout(block.layout),
|
|
3556
|
-
id: block.id,
|
|
3557
|
-
render: (layout) => (<ReportBlockCard accessIssues={accessIssues} block={block} blockTextOverride={blockTextOverrides[block.id]} chart={chart} chartSpecOverride={chart ? chartSpecOverrides[chart.id] : undefined} chartTextOverride={chart ? chartTextOverrides[chart.id] : undefined} chartTypeOverride={chart ? chartTypeOverrides[chart.id] : undefined} columnWidths={table ? tableColumnWidths[table.id] ?? {} : {}} filters={activeSurfaceFilters} isBlockMenuOpen={openMenuBlockId === block.id} isChartMenuOpen={Boolean(chart && openMenuChartId === chart.id)} isEditMode={activeEditMode} isTableMenuOpen={Boolean(table && openMenuTableId === table.id)} layout={layout} manifest={manifest} onBlockMenuOpenChange={(nextOpen) => {
|
|
3558
|
-
setOpenMenuChartId(null);
|
|
3559
|
-
setOpenMenuTableId(null);
|
|
3560
|
-
setOpenMenuBlockId(nextOpen ? block.id : null);
|
|
3561
|
-
}} onBlockTextChange={updateBlockText} onChartTypeChange={updateChartType} onChartMenuOpenChange={(nextOpen) => {
|
|
3562
|
-
setOpenMenuTableId(null);
|
|
3563
|
-
setOpenMenuBlockId(null);
|
|
3564
|
-
setOpenMenuChartId(chart && nextOpen ? chart.id : null);
|
|
3565
|
-
}} onChartModalOpen={setChartModal} onColumnWidthsChange={updateTableColumnWidths} onCopyResult={(message, isError = false) => setCopyMessage({ isError, message })} onDeleteBlock={capabilities.canEdit ? () => deleteReportBlock(block.id) : undefined} onRequestEditMode={requestEditMode} onTableMenuOpenChange={(nextOpen) => {
|
|
3566
|
-
setOpenMenuChartId(null);
|
|
3567
|
-
setOpenMenuBlockId(null);
|
|
3568
|
-
setOpenMenuTableId(table && nextOpen ? table.id : null);
|
|
3569
|
-
}} onTableModalOpen={setTableModal} onTableTextChange={updateTableText} onTextChange={updateChartText} selectedFilters={selectedFilters} snapshot={snapshot} table={table} tableTextOverride={table ? tableTextOverrides[table.id] : undefined}/>)
|
|
3570
|
-
};
|
|
3571
|
-
});
|
|
3572
|
-
}, [
|
|
3573
|
-
accessIssues,
|
|
3574
|
-
activeSurfaceFilters,
|
|
3575
|
-
activeEditMode,
|
|
3576
|
-
blockTextOverrides,
|
|
3577
|
-
cardsById,
|
|
3578
|
-
capabilities.canEdit,
|
|
3579
|
-
chartSpecOverrides,
|
|
3580
|
-
chartTextOverrides,
|
|
3581
|
-
chartTypeOverrides,
|
|
3582
|
-
chartsById,
|
|
3583
|
-
manifest,
|
|
3584
|
-
openMenuBlockId,
|
|
3585
|
-
openMenuChartId,
|
|
3586
|
-
openMenuTableId,
|
|
3587
|
-
requestEditMode,
|
|
3588
|
-
selectedFilters,
|
|
3589
|
-
snapshot,
|
|
3590
|
-
tableColumnWidths,
|
|
3591
|
-
tableTextOverrides,
|
|
3592
|
-
tablesById,
|
|
3593
|
-
visibleReportGridBlocks
|
|
3594
|
-
]);
|
|
3595
|
-
if (error) {
|
|
3596
|
-
return (<DashboardShell surface={manifest?.surface}>
|
|
3597
|
-
<div className="empty-state error-state">{error}</div>
|
|
3598
|
-
</DashboardShell>);
|
|
3599
|
-
}
|
|
3600
|
-
const activeFilters = activeFilterSummary(activeSurfaceFilters, selectedFilters);
|
|
3601
|
-
const chartModalSourceChart = chartModal
|
|
3602
|
-
? chartsById.get(chartModal.chart.id) ?? chartModal.chart
|
|
3603
|
-
: null;
|
|
3604
|
-
const chartModalBaseChart = chartModalSourceChart
|
|
3605
|
-
? applyChartSpecOverride(chartModalSourceChart, chartSpecOverrides[chartModalSourceChart.id])
|
|
3606
|
-
: null;
|
|
3607
|
-
const chartModalRows = chartModalBaseChart
|
|
3608
|
-
? filterRowsForDataset(getRows(snapshot, chartModalBaseChart.dataset), chartModalBaseChart.dataset, activeSurfaceFilters, selectedFilters, chartUsedFields(chartModalBaseChart))
|
|
3609
|
-
: [];
|
|
3610
|
-
const chartModalTypeOptions = chartModalBaseChart
|
|
3611
|
-
? compatibleChartTypesFor(chartModalBaseChart, chartModalRows)
|
|
3612
|
-
: [];
|
|
3613
|
-
const chartModalRequestedType = chartModalBaseChart
|
|
3614
|
-
? chartTypeOverrides[chartModalBaseChart.id] ?? chartModalBaseChart.type
|
|
3615
|
-
: null;
|
|
3616
|
-
const chartModalActiveType = chartModalRequestedType && chartModalTypeOptions.some((option) => option.type === chartModalRequestedType)
|
|
3617
|
-
? chartModalRequestedType
|
|
3618
|
-
: chartModalBaseChart?.type ?? null;
|
|
3619
|
-
const activeChartModal = chartModal?.kind === "fullscreen" && chartModalBaseChart && chartModalActiveType
|
|
3620
|
-
? {
|
|
3621
|
-
chart: withChartType({
|
|
3622
|
-
...chartModalBaseChart,
|
|
3623
|
-
subtitle: chartModal.chart.subtitle,
|
|
3624
|
-
title: chartModal.chart.title
|
|
3625
|
-
}, chartModalActiveType)
|
|
3626
|
-
}
|
|
3627
|
-
: null;
|
|
3628
|
-
if (isReport) {
|
|
3629
|
-
return (<DashboardShell isEditMode={activeEditMode} surface="report">
|
|
3630
|
-
<AnalyticsTopBar chrome={displayMode === "inline" ? "inline" : "full"} isEditMode={activeEditMode} manifest={manifest} onCancelEdit={cancelEditMode} onCopyResult={(message, isError = false) => setCopyMessage({ isError, message })} onEdit={requestEditMode} onRequestFullscreen={onRequestFullscreen} onSaveEdit={saveEditMode} onTitleChange={updatePageTitle} packageInfo={packageInfo} snapshot={snapshot} title={pageTitle}/>
|
|
3631
|
-
<AccessIssueStrip issues={accessIssues}/>
|
|
3632
|
-
{copyMessage ? (<div className={`copy-toast ${copyMessage.isError ? "error" : ""}`} role="status">
|
|
3633
|
-
{copyMessage.message}
|
|
3634
|
-
</div>) : null}
|
|
3635
|
-
|
|
3636
|
-
<AnalyticsLayoutCanvas ariaLabel="Report blocks" blocks={reportContentBlocks} cancelSelector={CARD_DRAG_CANCEL_SELECTOR} className="report-content-grid report-block-stack" isEditMode={activeEditMode} layoutResetKey={layoutResetKey} onLayoutChange={recordReportLayoutDraft} storageKey={reportStorageKey}/>
|
|
3637
|
-
{activeChartModal ? (<ChartDetailPage accessIssue={accessIssueForChart(activeChartModal.chart, accessIssues)} chart={activeChartModal.chart} filters={activeSurfaceFilters} manifest={manifest} onChartSpecChange={updateChartSpec} onClose={() => setChartModal(null)} packageInfo={packageInfo} rows={chartModalRows} selectedFilters={selectedFilters} snapshot={snapshot}/>) : null}
|
|
3638
|
-
{chartModal?.kind === "source" && chartModalBaseChart ? (<ChartSourceModalDialog activeFilters={activeFilters} chart={{
|
|
3639
|
-
...chartModalBaseChart,
|
|
3640
|
-
subtitle: chartModal.chart.subtitle,
|
|
3641
|
-
title: chartModal.chart.title
|
|
3642
|
-
}} manifest={manifest} onClose={() => setChartModal(null)} rows={chartModalRows} snapshot={snapshot}/>) : null}
|
|
3643
|
-
{tableModal ? (<TableModalDialog activeFilters={activeFilters} columnWidths={tableColumnWidths[tableModal.table.id] ?? {}} filters={activeSurfaceFilters} kind={tableModal.kind} manifest={manifest} onColumnWidthsChange={updateTableColumnWidths} onClose={() => setTableModal(null)} selectedFilters={selectedFilters} snapshot={snapshot} table={tableModal.table}/>) : null}
|
|
3644
|
-
</DashboardShell>);
|
|
3645
|
-
}
|
|
3646
|
-
return (<DashboardShell isEditMode={activeEditMode} surface={manifest?.surface}>
|
|
3647
|
-
<AnalyticsTopBar chrome={displayMode === "inline" ? "inline" : "full"} isEditMode={activeEditMode} manifest={manifest} onCancelEdit={cancelEditMode} onCopyResult={(message, isError = false) => setCopyMessage({ isError, message })} onEdit={requestEditMode} onRequestFullscreen={onRequestFullscreen} onSaveEdit={saveEditMode} onTitleChange={updatePageTitle} packageInfo={packageInfo} snapshot={snapshot} title={pageTitle}/>
|
|
3648
|
-
<AccessIssueStrip issues={accessIssues}/>
|
|
3649
|
-
{copyMessage ? (<div className={`copy-toast ${copyMessage.isError ? "error" : ""}`} role="status">
|
|
3650
|
-
{copyMessage.message}
|
|
3651
|
-
</div>) : null}
|
|
3652
|
-
|
|
3653
|
-
<FilterToolbar filters={activeSurfaceFilters} onChange={setSelectedFilters} selectedFilters={selectedFilters} snapshot={snapshot}/>
|
|
3654
|
-
|
|
3655
|
-
<KpiStrip cards={cards} filters={activeSurfaceFilters} selectedFilters={selectedFilters} snapshot={snapshot}/>
|
|
3656
|
-
|
|
3657
|
-
<AnalyticsLayoutCanvas ariaLabel="Dashboard content" blocks={dashboardContentBlocks} cancelSelector={CARD_DRAG_CANCEL_SELECTOR} className="dashboard-content-grid" isEditMode={activeEditMode} layoutResetKey={layoutResetKey} onLayoutChange={recordDashboardLayoutDraft} storageKey={storageKey}/>
|
|
3658
|
-
{activeChartModal ? (<ChartDetailPage accessIssue={accessIssueForChart(activeChartModal.chart, accessIssues)} chart={activeChartModal.chart} filters={activeSurfaceFilters} manifest={manifest} onChartSpecChange={updateChartSpec} onClose={() => setChartModal(null)} packageInfo={packageInfo} rows={chartModalRows} selectedFilters={selectedFilters} snapshot={snapshot}/>) : null}
|
|
3659
|
-
{chartModal?.kind === "source" && chartModalBaseChart ? (<ChartSourceModalDialog activeFilters={activeFilters} chart={{
|
|
3660
|
-
...chartModalBaseChart,
|
|
3661
|
-
subtitle: chartModal.chart.subtitle,
|
|
3662
|
-
title: chartModal.chart.title
|
|
3663
|
-
}} manifest={manifest} onClose={() => setChartModal(null)} rows={chartModalRows} snapshot={snapshot}/>) : null}
|
|
3664
|
-
{tableModal ? (<TableModalDialog activeFilters={activeFilters} columnWidths={tableColumnWidths[tableModal.table.id] ?? {}} filters={activeSurfaceFilters} kind={tableModal.kind} manifest={manifest} onColumnWidthsChange={updateTableColumnWidths} onClose={() => setTableModal(null)} selectedFilters={selectedFilters} snapshot={snapshot} table={tableModal.table}/>) : null}
|
|
3665
|
-
</DashboardShell>);
|
|
3666
|
-
}
|