@nurix/etna 0.19.11 → 0.19.13
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/README.md +6 -4
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/agents/maintenance.md +1 -1
- package/data/harnesses/code/harness.json +1 -1
- package/data/harnesses/code/rules/frontend.md +5 -5
- package/data/harnesses/code/skills/design/SKILL.md +70 -50
- package/data/harnesses/code/skills/design/components/charts.md +76 -0
- package/data/harnesses/code/skills/design/components/collections.md +132 -0
- package/data/harnesses/code/skills/design/components/feedback.md +47 -0
- package/data/harnesses/code/skills/design/components/hooks.md +35 -0
- package/data/harnesses/code/skills/design/components/index.md +143 -0
- package/data/harnesses/code/skills/design/components/inputs.md +327 -0
- package/data/harnesses/code/skills/design/components/layout.md +52 -0
- package/data/harnesses/code/skills/design/components/media.md +66 -0
- package/data/harnesses/code/skills/design/components/motion.md +82 -0
- package/data/harnesses/code/skills/design/components/navigation.md +29 -0
- package/data/harnesses/code/skills/design/components/overlays.md +72 -0
- package/data/harnesses/code/skills/design/components/setup.md +40 -0
- package/data/harnesses/code/skills/design/components/typography.md +46 -0
- package/data/harnesses/code/skills/design/components/utils.md +37 -0
- package/data/harnesses/code/skills/design/data/blueprints/browse.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/creation-flow.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/dashboard.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/grid.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/queue.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/record-detail.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/settings.md +38 -0
- package/data/harnesses/code/skills/design/data/chart-styling/Readme.md +33 -0
- package/data/harnesses/code/skills/design/data/chart-styling/carbon-accessible-alert-semantic.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/carbon-monochrome-sequential-trend.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/datadog-classic-six-aligned.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/datawrapper-tiered-gray-hierarchy.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/econ-red-rule-horizontal.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/financial-diverging-threshold-overlay.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/ft-vocabulary-ranked-muted.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/grafana-dark-threshold-bands.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/material-rounded-explicit-label.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/nyt-ghost-grid-direct-label.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/plot-currentcolor-inherit.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/saas-gradient-glow-area.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/shadcn-composable-css-vars.md +19 -0
- package/data/harnesses/code/skills/design/data/charts/census.csv +58 -0
- package/data/harnesses/code/skills/design/data/checklists/browse.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/creation-flow.md +20 -0
- package/data/harnesses/code/skills/design/data/checklists/dashboard.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/grid.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/queue.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/record-detail.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/settings.md +21 -0
- package/data/harnesses/code/skills/design/data/chrome/board-column.md +68 -0
- package/data/harnesses/code/skills/design/data/chrome/data-table.md +63 -0
- package/data/harnesses/code/skills/design/data/chrome/side-panel.md +60 -0
- package/data/harnesses/code/skills/design/data/chrome/sidebar.md +78 -0
- package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +62 -0
- package/data/harnesses/code/skills/design/data/chrome/topbar.md +65 -0
- package/data/harnesses/code/skills/design/data/color/accent-presets.md +90 -0
- package/data/harnesses/code/skills/design/data/color/aesop-earth-mute.md +91 -0
- package/data/harnesses/code/skills/design/data/color/arc-peach-play.md +90 -0
- package/data/harnesses/code/skills/design/data/color/attio-hunter-teal.md +90 -0
- package/data/harnesses/code/skills/design/data/color/carbon-cyan-signal.md +90 -0
- package/data/harnesses/code/skills/design/data/color/clay-parchment.md +90 -0
- package/data/harnesses/code/skills/design/data/color/duolingo-feather.md +90 -0
- package/data/harnesses/code/skills/design/data/color/geist-mono-signal.md +90 -0
- package/data/harnesses/code/skills/design/data/color/linear-graphite-violet.md +90 -0
- package/data/harnesses/code/skills/design/data/color/mercury-onyx-cobalt.md +90 -0
- package/data/harnesses/code/skills/design/data/color/notion-paper-ink.md +90 -0
- package/data/harnesses/code/skills/design/data/color/radix-slate-iris.md +90 -0
- package/data/harnesses/code/skills/design/data/color/raycast-void-spectrum.md +90 -0
- package/data/harnesses/code/skills/design/data/color/spectrum-celery-fuchsia.md +90 -0
- package/data/harnesses/code/skills/design/data/color/stripe-blurple-ruby.md +90 -0
- package/data/harnesses/code/skills/design/data/color/superhuman-cod-picton.md +89 -0
- package/data/harnesses/code/skills/design/data/corpora/operations-review/companies.csv +15 -0
- package/data/harnesses/code/skills/design/data/corpora/operations-review/corpus.md +87 -0
- package/data/harnesses/code/skills/design/data/corpora/operations-review/people.csv +25 -0
- package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/companies.csv +15 -0
- package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/corpus.md +83 -0
- package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/people.csv +25 -0
- package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/companies.csv +15 -0
- package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/corpus.md +89 -0
- package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/people.csv +25 -0
- package/data/harnesses/code/skills/design/data/elevation/ant-design-directional-shadow.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/apple-liquid-glass-material.md +18 -0
- package/data/harnesses/code/skills/design/data/elevation/atlassian-surface-shadow-pairing.md +20 -0
- package/data/harnesses/code/skills/design/data/elevation/fluent-2-key-ambient-ramp.md +20 -0
- package/data/harnesses/code/skills/design/data/elevation/github-primer-sparing-shadow.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/ibm-carbon-layer-and-border.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/linear-dark-surface-ladder.md +17 -0
- package/data/harnesses/code/skills/design/data/elevation/material-2-key-ambient-dp-ramp.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/material-3-tonal-elevation.md +21 -0
- package/data/harnesses/code/skills/design/data/elevation/neobrutalist-hard-offset.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/neumorphic-soft-dual-shadow.md +18 -0
- package/data/harnesses/code/skills/design/data/elevation/notion-warm-minimal-hairline.md +17 -0
- package/data/harnesses/code/skills/design/data/elevation/polaris-elevation-inset-bevel.md +21 -0
- package/data/harnesses/code/skills/design/data/elevation/radix-themes-alpha-ring-scale.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/shadcn-neutral-derived-radius.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/stripe-brand-tinted-dual-shadow.md +18 -0
- package/data/harnesses/code/skills/design/data/elevation/tailwind-default-utility-scale.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/vercel-geist-shadow-as-border.md +20 -0
- package/data/harnesses/code/skills/design/data/gradients/amber-attention-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/cloud-shadow-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/confident-lavender-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/ember-ship-flare.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/full-metal-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/indigo-ruby-cta-band.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/linear-graphite-wash.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/marble-quartz-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/midnight-aurora-panel.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/neutral-volume-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/revenue-uptrend-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/risk-decline-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/signal-blue-develop.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/slate-mist-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/stripe-atmosphere-mesh.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/teal-signal-accent.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/violet-magenta-promo.md +16 -0
- package/data/harnesses/code/skills/design/data/iconography.md +199 -0
- package/data/harnesses/code/skills/design/data/motion/accordion-expand.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/destructive-hold-confirm.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/drag-dismiss-momentum.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/empty-state-arrive.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/hover-lift.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/modal-center-arrive.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/number-ticker-roll.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/onboarding-bounce.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/panel-slide.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/press-feedback.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/progress-linear-loop.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/route-crossfade.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/rubber-band-overscroll.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/skeleton-shimmer.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/stagger-arrive.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/toast-enter-stack.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/tooltip-chain-instant.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/trigger-anchored-reveal.md +18 -0
- package/data/harnesses/code/skills/design/data/tells.csv +34 -0
- package/data/harnesses/code/skills/design/data/typography/anton-work-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/archivo-black-archivo.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/bitter-karla.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/dm-serif-dm-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/fraunces-inter.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/geist-geist-mono.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/hubot-sans-mona-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/ibm-plex-sans-operator-single-family.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/instrument-serif-instrument-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/inter-operator-single-family.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/libre-baskerville-libre-franklin.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/newsreader-inter.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/playfair-display-source-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/public-sans-operator-single-family.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/sohne-inter-fintech.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/source-serif-source-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/space-grotesk-work-sans-space-mono.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/spectral-manrope.md +17 -0
- package/data/harnesses/code/skills/design/data/voices/bright-collaborator.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/direct-operator.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/earnest-helper.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/editorial-composed.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/quiet-concierge.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/warm-guide.md +29 -0
- package/data/harnesses/code/skills/design/design-md/SCHEMA.md +179 -0
- package/data/harnesses/code/skills/design/design-md/index.csv +67 -0
- package/data/harnesses/code/skills/design/design-md/library/airbnb.md +207 -0
- package/data/harnesses/code/skills/design/design-md/library/airtable.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/anthropic-claude.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/apple.md +206 -0
- package/data/harnesses/code/skills/design/design-md/library/binance.md +210 -0
- package/data/harnesses/code/skills/design/design-md/library/bmw.md +210 -0
- package/data/harnesses/code/skills/design/design-md/library/cal.md +209 -0
- package/data/harnesses/code/skills/design/design-md/library/clay.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/clickhouse.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/cohere.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/coinbase.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/composio.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/cursor.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/elevenlabs.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/expo.md +200 -0
- package/data/harnesses/code/skills/design/design-md/library/figma.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/framer.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/hashicorp.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/ibm.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/intercom.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/kraken.md +200 -0
- package/data/harnesses/code/skills/design/design-md/library/linear-app.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/lovable.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/meta.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/minimax.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/mintlify.md +210 -0
- package/data/harnesses/code/skills/design/design-md/library/miro.md +209 -0
- package/data/harnesses/code/skills/design/design-md/library/mistral-ai.md +201 -0
- package/data/harnesses/code/skills/design/design-md/library/mongodb.md +201 -0
- package/data/harnesses/code/skills/design/design-md/library/notion.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/nvidia.md +215 -0
- package/data/harnesses/code/skills/design/design-md/library/ollama.md +213 -0
- package/data/harnesses/code/skills/design/design-md/library/opencode-ai.md +216 -0
- package/data/harnesses/code/skills/design/design-md/library/posthog.md +218 -0
- package/data/harnesses/code/skills/design/design-md/library/raycast.md +214 -0
- package/data/harnesses/code/skills/design/design-md/library/replicate.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/resend.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/revolut.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/runwayml.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/sanity.md +200 -0
- package/data/harnesses/code/skills/design/design-md/library/sentry.md +201 -0
- package/data/harnesses/code/skills/design/design-md/library/shopify.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/slack.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/spacex.md +206 -0
- package/data/harnesses/code/skills/design/design-md/library/spotify.md +206 -0
- package/data/harnesses/code/skills/design/design-md/library/stripe.md +196 -0
- package/data/harnesses/code/skills/design/design-md/library/supabase.md +196 -0
- package/data/harnesses/code/skills/design/design-md/library/superhuman.md +194 -0
- package/data/harnesses/code/skills/design/design-md/library/tesla.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/theverge.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/together-ai.md +193 -0
- package/data/harnesses/code/skills/design/design-md/library/uber.md +195 -0
- package/data/harnesses/code/skills/design/design-md/library/vercel.md +195 -0
- package/data/harnesses/code/skills/design/design-md/library/voltagent.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/warp.md +196 -0
- package/data/harnesses/code/skills/design/design-md/library/webflow.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/wired.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/wise.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/x-ai.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/zapier.md +198 -0
- package/data/harnesses/code/skills/design/design-md/nurix.md +214 -0
- package/data/harnesses/code/skills/design/design-md/variants/editorial-luxury.md +184 -0
- package/data/harnesses/code/skills/design/design-md/variants/expressive-saas.md +216 -0
- package/data/harnesses/code/skills/design/design-md/variants/quiet-premium.md +211 -0
- package/data/harnesses/code/skills/design/design-md/variants/terse-dense-operator.md +212 -0
- package/data/harnesses/code/skills/design/design-md/variants/warm-approachable.md +216 -0
- package/data/harnesses/code/skills/design/references/compose.md +23 -0
- package/data/harnesses/code/skills/design/references/content.md +28 -0
- package/data/harnesses/code/skills/design/references/emission.md +21 -0
- package/data/harnesses/code/skills/design/references/existing-apps.md +27 -0
- package/data/harnesses/code/skills/design/references/intake.md +36 -0
- package/data/harnesses/code/skills/design/references/object-pass.md +24 -0
- package/data/harnesses/code/skills/design/references/ship-gate.md +29 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/Readme.md +195 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/ai-layer.md +49 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/attributes-lifecycle.md +43 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/container-choice.md +44 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/creation-paths.md +46 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/dashboards.md +42 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/modes.md +42 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/object-model.md +46 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/page-archetype.md +48 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/promotion-demotion.md +37 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/settings.md +42 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/standard-pages.md +46 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/state-matrix.md +41 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/table-mechanics.md +48 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/verbs.md +45 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/viewer-context.md +40 -0
- package/data/harnesses/code/skills/design/scripts/README-validator.md +27 -0
- package/data/harnesses/code/skills/design/scripts/select-chart.mjs +596 -0
- package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +1198 -0
- package/data/harnesses/discussion/agents/discussion-baseline.md +1 -1
- package/data/harnesses/discussion/harness.json +1 -1
- package/data/harnesses/etna/agents/maintenance.md +1 -1
- package/data/harnesses/etna/harness.json +1 -1
- package/data/harnesses/hr/CLAUDE.md +1 -1
- package/data/harnesses/hr/harness.json +1 -1
- package/package.json +1 -1
- package/data/harnesses/code/skills/design/data/catalog/rules.csv +0 -137
- package/data/harnesses/code/skills/design/data/combinations/Readme.md +0 -33
- package/data/harnesses/code/skills/design/data/combinations/accent-presets.md +0 -113
- package/data/harnesses/code/skills/design/data/combinations/charts.csv +0 -33
- package/data/harnesses/code/skills/design/data/combinations/fonts-known.json +0 -448
- package/data/harnesses/code/skills/design/data/combinations/icons.csv +0 -157
- package/data/harnesses/code/skills/design/data/combinations/landing.csv +0 -38
- package/data/harnesses/code/skills/design/data/combinations/lucide-names.json +0 -2001
- package/data/harnesses/code/skills/design/data/combinations/manifest.json +0 -330
- package/data/harnesses/code/skills/design/data/combinations/motion.csv +0 -10
- package/data/harnesses/code/skills/design/data/combinations/palettes.csv +0 -92
- package/data/harnesses/code/skills/design/data/combinations/product-router.csv +0 -80
- package/data/harnesses/code/skills/design/data/combinations/styles.csv +0 -67
- package/data/harnesses/code/skills/design/data/combinations/typography.csv +0 -64
- package/data/harnesses/code/skills/design/data/design-md/SCHEMA.md +0 -106
- package/data/harnesses/code/skills/design/data/design-md/library/airbnb.md +0 -154
- package/data/harnesses/code/skills/design/data/design-md/library/airtable.md +0 -151
- package/data/harnesses/code/skills/design/data/design-md/library/anthropic-claude.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/apple.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/binance.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/bmw.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/cal.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/clay.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/clickhouse.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/cohere.md +0 -147
- package/data/harnesses/code/skills/design/data/design-md/library/coinbase.md +0 -156
- package/data/harnesses/code/skills/design/data/design-md/library/composio.md +0 -138
- package/data/harnesses/code/skills/design/data/design-md/library/cursor.md +0 -138
- package/data/harnesses/code/skills/design/data/design-md/library/elevenlabs.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/expo.md +0 -148
- package/data/harnesses/code/skills/design/data/design-md/library/figma.md +0 -141
- package/data/harnesses/code/skills/design/data/design-md/library/framer.md +0 -156
- package/data/harnesses/code/skills/design/data/design-md/library/hashicorp.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/ibm.md +0 -153
- package/data/harnesses/code/skills/design/data/design-md/library/intercom.md +0 -158
- package/data/harnesses/code/skills/design/data/design-md/library/kraken.md +0 -156
- package/data/harnesses/code/skills/design/data/design-md/library/linear-app.md +0 -122
- package/data/harnesses/code/skills/design/data/design-md/library/lovable.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/meta.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/minimax.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/mintlify.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/miro.md +0 -153
- package/data/harnesses/code/skills/design/data/design-md/library/mistral-ai.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/mongodb.md +0 -155
- package/data/harnesses/code/skills/design/data/design-md/library/notion.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/nvidia.md +0 -141
- package/data/harnesses/code/skills/design/data/design-md/library/ollama.md +0 -145
- package/data/harnesses/code/skills/design/data/design-md/library/opencode-ai.md +0 -142
- package/data/harnesses/code/skills/design/data/design-md/library/posthog.md +0 -152
- package/data/harnesses/code/skills/design/data/design-md/library/raycast.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/replicate.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/resend.md +0 -170
- package/data/harnesses/code/skills/design/data/design-md/library/revolut.md +0 -145
- package/data/harnesses/code/skills/design/data/design-md/library/runwayml.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/sanity.md +0 -168
- package/data/harnesses/code/skills/design/data/design-md/library/sentry.md +0 -163
- package/data/harnesses/code/skills/design/data/design-md/library/shopify.md +0 -147
- package/data/harnesses/code/skills/design/data/design-md/library/slack.md +0 -158
- package/data/harnesses/code/skills/design/data/design-md/library/spacex.md +0 -171
- package/data/harnesses/code/skills/design/data/design-md/library/spotify.md +0 -166
- package/data/harnesses/code/skills/design/data/design-md/library/stripe.md +0 -160
- package/data/harnesses/code/skills/design/data/design-md/library/supabase.md +0 -173
- package/data/harnesses/code/skills/design/data/design-md/library/superhuman.md +0 -145
- package/data/harnesses/code/skills/design/data/design-md/library/tesla.md +0 -153
- package/data/harnesses/code/skills/design/data/design-md/library/theverge.md +0 -167
- package/data/harnesses/code/skills/design/data/design-md/library/together-ai.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/uber.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/vercel.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/voltagent.md +0 -130
- package/data/harnesses/code/skills/design/data/design-md/library/warp.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/webflow.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/wired.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/wise.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/x-ai.md +0 -138
- package/data/harnesses/code/skills/design/data/design-md/library/zapier.md +0 -158
- package/data/harnesses/code/skills/design/data/design-md/nurix.md +0 -138
- package/data/harnesses/code/skills/design/data/design-tokens/SCHEMA.md +0 -695
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.tokens.json +0 -1272
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.tokens.json +0 -1273
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.tokens.json +0 -1262
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.tokens.json +0 -1257
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.tokens.json +0 -1261
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.tokens.json +0 -1281
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.tokens.json +0 -1262
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.tokens.json +0 -1271
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.tokens.json +0 -1293
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.tokens.json +0 -1267
- package/data/harnesses/code/skills/design/data/design-tokens/goldens.json +0 -251
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.tokens.json +0 -1273
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.tokens.json +0 -1286
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.tokens.json +0 -1290
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.resolver.json +0 -23
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.dark.json +0 -599
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.json +0 -1272
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.tokens.json +0 -1267
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.tokens.json +0 -1289
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.tokens.json +0 -1258
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.tokens.json +0 -1284
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.tokens.json +0 -1291
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.tokens.json +0 -1263
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.tokens.json +0 -1261
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.tokens.json +0 -1209
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.tokens.json +0 -1265
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.tokens.json +0 -1253
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.tokens.json +0 -1261
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.tokens.json +0 -1263
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.tokens.json +0 -1266
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.resolver.json +0 -23
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.dark.json +0 -623
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.tokens.json +0 -1264
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.tokens.json +0 -1288
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.tokens.json +0 -1272
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.tokens.json +0 -1265
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.tokens.json +0 -1287
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.tokens.json +0 -1286
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.tokens.json +0 -1285
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.tokens.json +0 -1287
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.tokens.json +0 -1278
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.tokens.json +0 -1269
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.tokens.json +0 -1285
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.tokens.json +0 -1303
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.tokens.json +0 -1274
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.tokens.json +0 -1296
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.resolver.json +0 -23
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.dark.json +0 -612
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.json +0 -1282
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.tokens.json +0 -1287
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.tokens.json +0 -1273
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.tokens.json +0 -1296
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.tokens.json +0 -1286
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/command-center.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/create-edit.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/dashboard.tsx +0 -225
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/entity-detail.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/list-detail.tsx +0 -224
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/log-stream.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/fixtures.md +0 -88
- package/data/harnesses/code/skills/design/data/operator-pack/surface-catalog.md +0 -132
- package/data/harnesses/code/skills/design/data/operator-pack/trust-rubric.md +0 -76
- package/data/harnesses/code/skills/design/references/create.md +0 -173
- package/data/harnesses/code/skills/design/references/diagnosis.md +0 -108
- package/data/harnesses/code/skills/design/references/hard-rules.md +0 -177
- package/data/harnesses/code/skills/design/references/improve.md +0 -116
- package/data/harnesses/code/skills/design/references/interaction.md +0 -77
- package/data/harnesses/code/skills/design/references/introspect.md +0 -100
- package/data/harnesses/code/skills/design/references/lifecycle.md +0 -73
- package/data/harnesses/code/skills/design/references/personality.md +0 -575
- package/data/harnesses/code/skills/design/references/structure.md +0 -159
- package/data/harnesses/code/skills/design/scripts/convert-design-md.mjs +0 -1312
- package/data/harnesses/code/skills/design/scripts/derive-personality.mjs +0 -1498
- package/data/harnesses/code/skills/design/scripts/generate-fixtures.mjs +0 -551
- package/data/harnesses/code/skills/design/scripts/lib/checks.mjs +0 -1092
- package/data/harnesses/code/skills/design/scripts/lib/golden.mjs +0 -26
- package/data/harnesses/code/skills/design/scripts/lib/vocabulary.mjs +0 -65
- package/data/harnesses/code/skills/design/scripts/phase-gate.mjs +0 -538
- package/data/harnesses/code/skills/design/scripts/preship-sweep.mjs +0 -312
- package/data/harnesses/code/skills/design/scripts/render-rung.mjs +0 -890
- package/data/harnesses/code/skills/design/scripts/scaffold.mjs +0 -769
- package/data/harnesses/code/skills/design/scripts/self-integrity.mjs +0 -675
- package/data/harnesses/code/skills/design/scripts/snapshot-goldens.mjs +0 -164
- package/data/harnesses/code/skills/design/scripts/validate-combinations.mjs +0 -439
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
// skills/design/data/operator-pack/exemplars/command-center.tsx — reference render — imitate the ambition and the composition; the project's tokens supply the look.
|
|
2
|
-
// Surface: baseline
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
4
|
-
import { Activity, AlertTriangle, ArrowDown, ArrowUp, Hand, Radio, RefreshCw, ShieldAlert, Signal, Undo2, WifiOff, type LucideIcon } from "lucide-react";
|
|
5
|
-
import { Button } from "@nurix/components/button";
|
|
6
|
-
import { Badge } from "@nurix/components/badge";
|
|
7
|
-
import { Card } from "@nurix/components/card";
|
|
8
|
-
import { Skeleton } from "@nurix/components/skeleton";
|
|
9
|
-
import { EmptyState } from "@nurix/components/empty-state";
|
|
10
|
-
import { Alert, AlertTitle, AlertDescription } from "@nurix/components/alert";
|
|
11
|
-
import { ScrollArea } from "@nurix/components/scroll-area";
|
|
12
|
-
import { Sparkline } from "@nurix/components/sparkline";
|
|
13
|
-
import { Kbd } from "@nurix/components/kbd";
|
|
14
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, DialogClose } from "@nurix/components/dialog";
|
|
15
|
-
import { Statistics, StatisticsHeader, StatisticsHeading, StatisticsTitle, StatisticsMetric, StatisticsValue, StatisticsUnit, StatisticsTrend } from "@nurix/components/statistics";
|
|
16
|
-
type SurfaceState = "loading" | "ready" | "error";
|
|
17
|
-
type PanelStatus = "ok" | "degraded" | "down" | "loading";
|
|
18
|
-
type Sentiment = "negative" | "neutral" | "positive";
|
|
19
|
-
interface HealthPanel { key: string; label: string; status: PanelStatus; p95: number; trend: number[]; since: string; }
|
|
20
|
-
interface StreamEvent { id: string; at: string; session: string; kind: "started" | "escalated" | "resolved" | "dropped"; detail: string; }
|
|
21
|
-
interface Escalation { id: string; session: string; reason: string; sentiment: Sentiment; waitingSec: number; }
|
|
22
|
-
export interface CommandCenterProps {
|
|
23
|
-
state?: SurfaceState;
|
|
24
|
-
panels?: HealthPanel[];
|
|
25
|
-
events?: StreamEvent[];
|
|
26
|
-
escalations?: Escalation[];
|
|
27
|
-
updatedAt?: string;
|
|
28
|
-
onRetry?: () => void;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const STATUS_TONE: Record<PanelStatus, { text: string; word: string; Icon: LucideIcon }> = {
|
|
32
|
-
ok: { text: "text-success", word: "healthy", Icon: Signal },
|
|
33
|
-
degraded: { text: "text-warning", word: "degraded", Icon: AlertTriangle },
|
|
34
|
-
down: { text: "text-destructive", word: "down", Icon: WifiOff },
|
|
35
|
-
loading: { text: "text-muted-foreground", word: "probing", Icon: RefreshCw },
|
|
36
|
-
};
|
|
37
|
-
const SENTIMENT_TONE: Record<Sentiment, string> = { negative: "text-destructive", neutral: "text-muted-foreground", positive: "text-success" };
|
|
38
|
-
const KIND_LABEL: Record<StreamEvent["kind"], string> = { started: "Session started", escalated: "Escalated", resolved: "Resolved", dropped: "Call dropped" };
|
|
39
|
-
|
|
40
|
-
// Format a waiting duration in seconds as a compact mm:ss string.
|
|
41
|
-
function fmtWait(sec: number): string {
|
|
42
|
-
return `${Math.floor(sec / 60)}:${String(sec % 60).padStart(2, "0")}`;
|
|
43
|
-
}
|
|
44
|
-
// One KPI tile in the health header; the numeric value carries tabular figures.
|
|
45
|
-
function KpiTile({ label, value, unit, trend }: { label: string; value: string; unit?: string; trend?: number }) {
|
|
46
|
-
return (
|
|
47
|
-
<Statistics variant="card" size="md">
|
|
48
|
-
<StatisticsHeader><StatisticsHeading><StatisticsTitle size="sm">{label}</StatisticsTitle></StatisticsHeading></StatisticsHeader>
|
|
49
|
-
<StatisticsMetric>
|
|
50
|
-
<StatisticsValue className="tabular-nums">{value}</StatisticsValue>
|
|
51
|
-
{unit ? <StatisticsUnit>{unit}</StatisticsUnit> : null}
|
|
52
|
-
{trend !== undefined ? <StatisticsTrend value={trend} pill /> : null}
|
|
53
|
-
</StatisticsMetric>
|
|
54
|
-
</Statistics>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
// One subsystem health panel; loads, degrades, and fails independently and names which.
|
|
58
|
-
function HealthTile({ panel }: { panel: HealthPanel }) {
|
|
59
|
-
if (panel.status === "loading") {
|
|
60
|
-
return (
|
|
61
|
-
<Card className="flex flex-col gap-3 p-4" role="status" aria-busy="true">
|
|
62
|
-
<Skeleton className="h-4 w-24" /><Skeleton className="h-7 w-16" /><Skeleton className="h-7 w-full" />
|
|
63
|
-
</Card>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
const tone = STATUS_TONE[panel.status];
|
|
67
|
-
return (
|
|
68
|
-
<Card className="flex flex-col gap-2 p-4">
|
|
69
|
-
<div className="flex items-center justify-between">
|
|
70
|
-
<span className="text-small font-medium text-foreground">{panel.label}</span>
|
|
71
|
-
<span className={`flex items-center gap-1 text-caption ${tone.text}`}><tone.Icon className="size-4" aria-hidden />{tone.word}</span>
|
|
72
|
-
</div>
|
|
73
|
-
<div className="flex items-baseline gap-1">
|
|
74
|
-
<span className="text-h3 tabular-nums text-foreground">{panel.p95}</span>
|
|
75
|
-
<span className="text-caption text-muted-foreground">ms p95</span>
|
|
76
|
-
</div>
|
|
77
|
-
<Sparkline points={panel.trend} type="line" height={28} className="w-full text-primary" />
|
|
78
|
-
{panel.status === "degraded" ? <p className="text-caption text-warning">Stale since {panel.since}; showing last known values.</p> : null}
|
|
79
|
-
{panel.status === "down" ? <p className="text-caption text-destructive">Probe timed out {panel.since} ago; retrying automatically.</p> : null}
|
|
80
|
-
</Card>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
// The live session-event stream; additions announce politely without stealing focus.
|
|
84
|
-
function StreamPane({ events }: { events: StreamEvent[] }) {
|
|
85
|
-
return (
|
|
86
|
-
<Card className="flex min-h-0 flex-col">
|
|
87
|
-
<div className="flex items-center gap-2 border-b border-border px-4 py-3">
|
|
88
|
-
<Activity className="size-4 text-primary" aria-hidden />
|
|
89
|
-
<h2 className="text-h3">Live session stream</h2>
|
|
90
|
-
<Badge variant="outline" className="ml-auto tabular-nums">{events.length}</Badge>
|
|
91
|
-
</div>
|
|
92
|
-
<ScrollArea className="min-h-0 flex-1">
|
|
93
|
-
{/* w-0 min-w-full pins the list to the ScrollArea viewport, so truncation paints at the visible edge */}
|
|
94
|
-
<ul aria-live="polite" aria-relevant="additions" className="w-0 min-w-full divide-y divide-border">
|
|
95
|
-
{events.map((e) => (
|
|
96
|
-
<li key={e.id} className="flex items-baseline gap-3 px-4 py-2 text-small">
|
|
97
|
-
<time className="shrink-0 font-mono tabular-nums text-muted-foreground">{e.at}</time>
|
|
98
|
-
<span className="shrink-0 font-mono text-muted-foreground">{e.session}</span>
|
|
99
|
-
<span className="shrink-0 text-foreground">{KIND_LABEL[e.kind]}</span>
|
|
100
|
-
<span className="min-w-0 flex-1 truncate text-muted-foreground" title={e.detail}>{e.detail}</span>
|
|
101
|
-
</li>
|
|
102
|
-
))}
|
|
103
|
-
</ul>
|
|
104
|
-
</ScrollArea>
|
|
105
|
-
</Card>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
// The escalation queue: a sortable, keyboard-actionable table of sessions flagged for human takeover.
|
|
109
|
-
function EscalationTable({ rows, sort, onToggleSort, onTakeOver, onDismiss }: { rows: Escalation[]; sort: "asc" | "desc"; onToggleSort: () => void; onTakeOver: (e: Escalation) => void; onDismiss: (e: Escalation) => void }) {
|
|
110
|
-
return (
|
|
111
|
-
<table className="w-full border-collapse text-small">
|
|
112
|
-
<thead className="sticky top-0 bg-card">
|
|
113
|
-
<tr className="border-b border-border text-left text-caption text-muted-foreground">
|
|
114
|
-
<th scope="col" className="px-3 py-2 font-medium">Session</th>
|
|
115
|
-
<th scope="col" className="px-3 py-2 font-medium">Reason</th>
|
|
116
|
-
<th scope="col" className="px-3 py-2 font-medium">Sentiment</th>
|
|
117
|
-
<th scope="col" aria-sort={sort === "asc" ? "ascending" : "descending"} className="px-3 py-2 text-right font-medium">
|
|
118
|
-
<button type="button" onClick={onToggleSort} className="ml-auto inline-flex items-center gap-1 rounded focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">Waiting{sort === "asc" ? <ArrowUp className="size-4" aria-hidden /> : <ArrowDown className="size-4" aria-hidden />}</button>
|
|
119
|
-
</th>
|
|
120
|
-
<th scope="col" className="px-3 py-2 text-right font-medium">Actions</th>
|
|
121
|
-
</tr>
|
|
122
|
-
</thead>
|
|
123
|
-
<tbody>
|
|
124
|
-
{rows.map((e) => (
|
|
125
|
-
<tr key={e.id} className="motion-lift border-b border-border">
|
|
126
|
-
<td className="min-w-[9ch] px-3 py-2"><span className="whitespace-nowrap font-mono tabular-nums text-foreground">{e.session}</span></td>
|
|
127
|
-
<td className="px-3 py-2 text-muted-foreground"><span className="line-clamp-1">{e.reason}</span></td>
|
|
128
|
-
<td className="px-3 py-2"><span className={SENTIMENT_TONE[e.sentiment]}>{e.sentiment}</span></td>
|
|
129
|
-
<td className="px-3 py-2 text-right"><span className="font-mono tabular-nums text-foreground">{fmtWait(e.waitingSec)}</span></td>
|
|
130
|
-
<td className="px-3 py-2">
|
|
131
|
-
<div className="flex items-center justify-end gap-2">
|
|
132
|
-
<Button size="sm" onClick={() => onTakeOver(e)}><Hand className="size-4" aria-hidden />Take over</Button>
|
|
133
|
-
<Button size="sm" variant="ghost" onClick={() => onDismiss(e)}>Dismiss</Button>
|
|
134
|
-
</div>
|
|
135
|
-
</td>
|
|
136
|
-
</tr>
|
|
137
|
-
))}
|
|
138
|
-
</tbody>
|
|
139
|
-
</table>
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
// The whole-surface loading state: skeleton tiles mirror the KPI, health, and pane grid.
|
|
143
|
-
function CommandSkeleton() {
|
|
144
|
-
return (
|
|
145
|
-
<div className="flex flex-1 flex-col gap-4" role="status" aria-busy="true">
|
|
146
|
-
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">{[0, 1, 2, 3].map((i) => <Skeleton key={i} className="h-20 w-full" />)}</div>
|
|
147
|
-
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">{[0, 1, 2, 3].map((i) => <Skeleton key={i} className="h-28 w-full" />)}</div>
|
|
148
|
-
<div className="grid min-h-64 flex-1 grid-cols-1 gap-4 lg:grid-cols-2"><Skeleton className="h-full w-full" /><Skeleton className="h-full w-full" /></div>
|
|
149
|
-
</div>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
// Voice-AI live-ops bridge: monitor concurrent sessions, detect escalations, intervene under time pressure.
|
|
153
|
-
export function CommandCenter({ state = "ready", panels = SEED_PANELS, events = SEED_EVENTS, escalations = SEED_ESCALATIONS, updatedAt = "14:32:07", onRetry }: CommandCenterProps) {
|
|
154
|
-
const [sort, setSort] = useState<"asc" | "desc">("desc");
|
|
155
|
-
const [active, setActive] = useState<Escalation | null>(null);
|
|
156
|
-
const [handled, setHandled] = useState<{ id: string; session: string; verb: string } | null>(null);
|
|
157
|
-
const rows = useMemo(() => {
|
|
158
|
-
const visible = handled ? escalations.filter((e) => e.id !== handled.id) : escalations;
|
|
159
|
-
return [...visible].sort((a, b) => (sort === "asc" ? a.waitingSec - b.waitingSec : b.waitingSec - a.waitingSec));
|
|
160
|
-
}, [escalations, handled, sort]);
|
|
161
|
-
return (
|
|
162
|
-
<main data-density-step="tighter" className="flex min-h-[100dvh] flex-col gap-4 bg-background p-4 font-sans text-foreground">
|
|
163
|
-
<header className="flex flex-wrap items-center gap-3">
|
|
164
|
-
<Radio className="size-5 text-primary" aria-hidden />
|
|
165
|
-
<h1 className="text-h1">Voice operations bridge</h1>
|
|
166
|
-
<span className="flex items-center gap-1 text-small text-success"><Signal className="size-4" aria-hidden />Live</span>
|
|
167
|
-
<span className="ml-auto flex items-center gap-2 text-small text-muted-foreground">Updated <time className="font-mono tabular-nums">{updatedAt}</time><Kbd>/</Kbd>to search</span>
|
|
168
|
-
</header>
|
|
169
|
-
{state === "loading" ? <CommandSkeleton /> : state === "error" ? (
|
|
170
|
-
<div className="flex flex-1 items-center justify-center">
|
|
171
|
-
<Alert variant="destructive" className="max-w-md">
|
|
172
|
-
<AlertTitle>Telemetry feed unavailable</AlertTitle>
|
|
173
|
-
<AlertDescription>The bridge lost its link to the session gateway, so live signal is not arriving. Reconnect to resume monitoring.</AlertDescription>
|
|
174
|
-
<Button size="sm" variant="outline" onClick={onRetry} className="mt-3"><RefreshCw className="size-4" aria-hidden />Reconnect</Button>
|
|
175
|
-
</Alert>
|
|
176
|
-
</div>
|
|
177
|
-
) : (
|
|
178
|
-
<>
|
|
179
|
-
<section aria-label="Key metrics" className="grid grid-cols-2 gap-4 lg:grid-cols-4">{/* mock: illustrative KPI figures */}
|
|
180
|
-
<KpiTile label="Active sessions" value="128" trend={6} />
|
|
181
|
-
<KpiTile label="Escalations waiting" value={String(rows.length)} trend={-3} />
|
|
182
|
-
<KpiTile label="Median handle time" value="2:41" unit="min" />
|
|
183
|
-
<KpiTile label="Containment rate" value="72" unit="%" trend={2} />
|
|
184
|
-
</section>
|
|
185
|
-
<section aria-label="Subsystem health" className="grid grid-cols-2 gap-4 lg:grid-cols-4">
|
|
186
|
-
{panels.map((p) => <HealthTile key={p.key} panel={p} />)}
|
|
187
|
-
</section>
|
|
188
|
-
<section aria-label="Live operations" className="grid min-h-0 flex-1 grid-cols-1 gap-4 lg:grid-cols-2">
|
|
189
|
-
<StreamPane events={events} />
|
|
190
|
-
<Card className="flex min-h-0 flex-col">
|
|
191
|
-
<div className="flex items-center gap-2 border-b border-border px-4 py-3">
|
|
192
|
-
<ShieldAlert className="size-4 text-primary" aria-hidden />
|
|
193
|
-
<h2 className="text-h3">Escalation queue</h2>
|
|
194
|
-
<Badge variant="outline" className="ml-auto tabular-nums">{rows.length}</Badge>
|
|
195
|
-
</div>
|
|
196
|
-
<ScrollArea className="min-h-0 flex-1">
|
|
197
|
-
{rows.length === 0 ? (
|
|
198
|
-
<EmptyState icon={<ShieldAlert className="size-8 text-muted-foreground" aria-hidden />} title="No escalations waiting" description="Every live session is being handled by the AI agent. Flagged sessions surface here for human takeover." />
|
|
199
|
-
) : (
|
|
200
|
-
<EscalationTable rows={rows} sort={sort} onToggleSort={() => setSort((s) => (s === "asc" ? "desc" : "asc"))} onTakeOver={setActive} onDismiss={(e) => setHandled({ id: e.id, session: e.session, verb: "Dismissed" })} />
|
|
201
|
-
)}
|
|
202
|
-
</ScrollArea>
|
|
203
|
-
</Card>
|
|
204
|
-
</section>
|
|
205
|
-
{handled ? (
|
|
206
|
-
<div role="status" className="flex items-center gap-3 rounded-md border border-border bg-muted px-4 py-2 text-small text-foreground">
|
|
207
|
-
<span>{handled.verb} session <span className="font-mono tabular-nums">{handled.session}</span>.</span>
|
|
208
|
-
<Button size="sm" variant="ghost" className="ml-auto" onClick={() => setHandled(null)}><Undo2 className="size-4" aria-hidden />Undo</Button>
|
|
209
|
-
</div>
|
|
210
|
-
) : null}
|
|
211
|
-
</>
|
|
212
|
-
)}
|
|
213
|
-
<Dialog open={!!active} onOpenChange={(open) => { if (!open) setActive(null); }}>
|
|
214
|
-
<DialogContent variant="danger" size="sm">
|
|
215
|
-
<DialogHeader>
|
|
216
|
-
<DialogTitle>Take over session {active?.session}?</DialogTitle>
|
|
217
|
-
<DialogDescription>The AI agent will be muted and the caller connected to a human. The current turn is interrupted immediately.</DialogDescription>
|
|
218
|
-
</DialogHeader>
|
|
219
|
-
<DialogBody className="text-small text-muted-foreground">Flagged for: {active?.reason} Waiting {active ? fmtWait(active.waitingSec) : ""}.</DialogBody>
|
|
220
|
-
<DialogFooter>
|
|
221
|
-
<DialogClose asChild><Button variant="ghost" size="sm">Cancel</Button></DialogClose>
|
|
222
|
-
<Button size="sm" onClick={() => { if (active) setHandled({ id: active.id, session: active.session, verb: "Took over" }); setActive(null); }}><Hand className="size-4" aria-hidden />Take over session</Button>
|
|
223
|
-
</DialogFooter>
|
|
224
|
-
</DialogContent>
|
|
225
|
-
</Dialog>
|
|
226
|
-
</main>
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
// exemplar fixture data — seeded, domain-shaped
|
|
230
|
-
const SEED_PANELS: HealthPanel[] = [
|
|
231
|
-
{ key: "asr", label: "Speech recognition", status: "ok", p95: 214, trend: [190, 205, 198, 212, 208, 214], since: "" },
|
|
232
|
-
{ key: "llm", label: "Language model", status: "degraded", p95: 1180, trend: [420, 610, 880, 1020, 1140, 1180], since: "14:29" },
|
|
233
|
-
{ key: "tts", label: "Speech synthesis", status: "ok", p95: 96, trend: [88, 92, 90, 94, 91, 96], since: "" },
|
|
234
|
-
{ key: "tel", label: "Telephony gateway", status: "down", p95: 0, trend: [140, 150, 0, 0, 0, 0], since: "2m" },
|
|
235
|
-
];
|
|
236
|
-
const SEED_EVENTS: StreamEvent[] = [
|
|
237
|
-
{ id: "v1", at: "14:32:07", session: "cs-4821", kind: "escalated", detail: "Three low-confidence recognition turns on a billing dispute; caller growing frustrated." },
|
|
238
|
-
{ id: "v2", at: "14:32:01", session: "cs-4790", kind: "resolved", detail: "Refund confirmed, transcript archived." },
|
|
239
|
-
{ id: "v3", at: "14:31:44", session: "cs-4818", kind: "started", detail: "Inbound, plan upgrade intent." },
|
|
240
|
-
{ id: "v4", at: "14:31:22", session: "cs-4802", kind: "dropped", detail: "Carrier RTP timeout on the telephony gateway before the greeting completed." },
|
|
241
|
-
{ id: "v5", at: "14:31:05", session: "cs-4795", kind: "escalated", detail: "Explicit request for a human agent." },
|
|
242
|
-
];
|
|
243
|
-
const SEED_ESCALATIONS: Escalation[] = [
|
|
244
|
-
{ id: "e-01", session: "cs-4821", reason: "Three low-confidence recognition turns on a billing dispute; caller growing frustrated.", sentiment: "negative", waitingSec: 187 },
|
|
245
|
-
{ id: "e-02", session: "cs-4795", reason: "Explicit request for a human agent.", sentiment: "neutral", waitingSec: 94 },
|
|
246
|
-
{ id: "e-03", session: "cs-4766", reason: "Sentiment dropped after a failed address verification.", sentiment: "negative", waitingSec: 63 },
|
|
247
|
-
{ id: "e-04", session: "cs-4740", reason: "Refund amount exceeds the agent's autonomous limit.", sentiment: "neutral", waitingSec: 41 },
|
|
248
|
-
{ id: "e-05", session: "cs-4712", reason: "Caller confirmed resolution; awaiting supervisor sign-off.", sentiment: "positive", waitingSec: 12 },
|
|
249
|
-
];
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
// skills/design/data/operator-pack/exemplars/create-edit.tsx — reference render — imitate the ambition and the composition; the project's tokens supply the look.
|
|
2
|
-
// Surface: brand-forward (delight-allowing)
|
|
3
|
-
import { useState, type FormEvent, type ReactNode } from "react";
|
|
4
|
-
import { TriangleAlert, CircleCheck, Plus, RotateCcw } from "lucide-react";
|
|
5
|
-
import { Sheet, SheetContent, SheetHeader, SheetBody, SheetFooter, SheetTitle, SheetDescription } from "@nurix/components/sheet";
|
|
6
|
-
import { Button } from "@nurix/components/button";
|
|
7
|
-
import { Input } from "@nurix/components/input";
|
|
8
|
-
import { NumberInput } from "@nurix/components/number-input";
|
|
9
|
-
import { Switch } from "@nurix/components/switch";
|
|
10
|
-
import { Checkbox } from "@nurix/components/checkbox";
|
|
11
|
-
import { Label } from "@nurix/components/label";
|
|
12
|
-
import { Badge } from "@nurix/components/badge";
|
|
13
|
-
import { Kbd } from "@nurix/components/kbd";
|
|
14
|
-
import { Skeleton } from "@nurix/components/skeleton";
|
|
15
|
-
import { EmptyState } from "@nurix/components/empty-state";
|
|
16
|
-
import { Alert, AlertTitle, AlertDescription } from "@nurix/components/alert";
|
|
17
|
-
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@nurix/components/native-select";
|
|
18
|
-
import { RadioGroupSegmented, RadioGroupSegmentedItem } from "@nurix/components/radio-group";
|
|
19
|
-
|
|
20
|
-
export type RuleSurfaceStatus = "loading" | "empty" | "error" | "degraded" | "ready";
|
|
21
|
-
export type ThresholdDirection = "above" | "below";
|
|
22
|
-
type FormPhase = "form" | "preview" | "success";
|
|
23
|
-
type SubmitStatus = "idle" | "submitting" | "failed";
|
|
24
|
-
type FieldErrors = Partial<Record<keyof AlertRuleDraft, string>>;
|
|
25
|
-
|
|
26
|
-
export interface NotifyChannel { id: string; label: string; kind: "email" | "webhook" | "pager"; }
|
|
27
|
-
export interface MetricOption { slug: string; label: string; unit: string; }
|
|
28
|
-
export interface SubmitOutcome { ok: boolean; message?: string; }
|
|
29
|
-
|
|
30
|
-
export interface AlertRuleDraft {
|
|
31
|
-
name: string; metricSlug: string; direction: ThresholdDirection;
|
|
32
|
-
threshold: number | null; channelIds: string[]; enabled: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CreateEditRuleSurfaceProps {
|
|
36
|
-
mode: "create" | "edit"; status: RuleSurfaceStatus; open: boolean;
|
|
37
|
-
metrics: MetricOption[]; channels: NotifyChannel[]; metricsCachedAt?: string | null;
|
|
38
|
-
initial?: Partial<AlertRuleDraft>; onSubmit: (draft: AlertRuleDraft) => Promise<SubmitOutcome>;
|
|
39
|
-
onCancel: () => void; onAddChannel: () => void; onRetryLoad: () => void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const BLANK_DRAFT: AlertRuleDraft = { name: "", metricSlug: "", direction: "above", threshold: null, channelIds: [], enabled: true };
|
|
43
|
-
|
|
44
|
-
// Collect blocking errors for the whole draft; re-run on every render so blur and submit share one source.
|
|
45
|
-
function validate(draft: AlertRuleDraft): FieldErrors {
|
|
46
|
-
const errors: FieldErrors = {};
|
|
47
|
-
if (draft.name.trim().length < 3) errors.name = "Name the rule (3 characters or more) so it's findable in the alert list.";
|
|
48
|
-
if (!draft.metricSlug) errors.metricSlug = "Choose the metric this rule watches.";
|
|
49
|
-
if (draft.threshold == null) errors.threshold = "Set the threshold value to compare against.";
|
|
50
|
-
if (draft.channelIds.length === 0) errors.channelIds = "Add at least one channel so alerts have somewhere to go.";
|
|
51
|
-
return errors;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Label-over-input wrapper: the visible label, the control, then an inline error (or hint) below.
|
|
55
|
-
function Field({ id, label, hint, error, htmlFor, children }: { id: string; label: string; hint?: string; error?: string; htmlFor?: string; children: ReactNode }) {
|
|
56
|
-
return (
|
|
57
|
-
<div className="grid gap-1.5">
|
|
58
|
-
<Label id={`${id}-label`} htmlFor={htmlFor} className="text-small font-medium">{label}</Label>
|
|
59
|
-
{children}
|
|
60
|
-
{error ? <p id={`${id}-error`} className="text-small text-destructive">{error}</p> : hint ? <p className="text-small text-muted-foreground">{hint}</p> : null}
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
// One label/value row inside the preview summary.
|
|
65
|
-
function PreviewRow({ label, children }: { label: string; children: ReactNode }) {
|
|
66
|
-
return (
|
|
67
|
-
<div className="grid grid-cols-[8rem_1fr] gap-4">
|
|
68
|
-
<dt className="text-muted-foreground">{label}</dt>
|
|
69
|
-
<dd className="text-foreground">{children}</dd>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
// The create/edit alert-rule surface: a side sheet that opens beside the alert list and returns to it on close.
|
|
74
|
-
export function CreateEditRuleSurface(props: CreateEditRuleSurfaceProps) {
|
|
75
|
-
const { mode, status, open, metrics, channels, metricsCachedAt, onSubmit, onCancel, onAddChannel, onRetryLoad } = props;
|
|
76
|
-
const [draft, setDraft] = useState<AlertRuleDraft>(() => ({ ...BLANK_DRAFT, ...props.initial }));
|
|
77
|
-
const [touched, setTouched] = useState<Record<string, boolean>>({});
|
|
78
|
-
const [attempted, setAttempted] = useState(false);
|
|
79
|
-
const [phase, setPhase] = useState<FormPhase>("form");
|
|
80
|
-
const [submitStatus, setSubmitStatus] = useState<SubmitStatus>("idle");
|
|
81
|
-
const [submitError, setSubmitError] = useState<string | null>(null);
|
|
82
|
-
const errors = validate(draft);
|
|
83
|
-
const metric = metrics.find((m) => m.slug === draft.metricSlug);
|
|
84
|
-
const selectedChannels = channels.filter((c) => draft.channelIds.includes(c.id));
|
|
85
|
-
const isForm = status === "ready" || status === "degraded";
|
|
86
|
-
const busy = submitStatus === "submitting";
|
|
87
|
-
function patch<K extends keyof AlertRuleDraft>(key: K, value: AlertRuleDraft[K]) { setDraft((d) => ({ ...d, [key]: value })); }
|
|
88
|
-
function blur(key: keyof AlertRuleDraft) { setTouched((t) => ({ ...t, [key]: true })); }
|
|
89
|
-
const show = (key: keyof AlertRuleDraft): string | undefined => ((attempted || touched[key]) && errors[key]) || undefined;
|
|
90
|
-
// Validate the whole draft; a clean draft advances to the preview-then-commit step.
|
|
91
|
-
function handlePreview(event: FormEvent) {
|
|
92
|
-
event.preventDefault();
|
|
93
|
-
setAttempted(true);
|
|
94
|
-
setSubmitError(null);
|
|
95
|
-
if (Object.keys(errors).length === 0) setPhase("preview");
|
|
96
|
-
}
|
|
97
|
-
// Commit the reviewed draft; failure returns to the form with entries preserved and the reason inline.
|
|
98
|
-
async function handleConfirm() {
|
|
99
|
-
setSubmitStatus("submitting");
|
|
100
|
-
const outcome = await onSubmit(draft);
|
|
101
|
-
if (outcome.ok) { setSubmitStatus("idle"); setPhase("success"); }
|
|
102
|
-
else { setSubmitStatus("failed"); setSubmitError(outcome.message ?? "The alerting service rejected the rule."); setPhase("form"); }
|
|
103
|
-
}
|
|
104
|
-
function reset() { setDraft({ ...BLANK_DRAFT }); setTouched({}); setAttempted(false); setSubmitStatus("idle"); setSubmitError(null); setPhase("form"); }
|
|
105
|
-
return (
|
|
106
|
-
<Sheet open={open} onOpenChange={(next) => { if (!next) onCancel(); }}>
|
|
107
|
-
<SheetContent side="right" size="lg" data-density-step="looser" className="bg-surface-overlay font-sans shadow-overlay">
|
|
108
|
-
<SheetHeader showDivider>
|
|
109
|
-
<SheetTitle className="text-h3">{mode === "create" ? "New alert rule" : "Edit alert rule"}</SheetTitle>
|
|
110
|
-
<SheetDescription className="text-small">Watches one metric and notifies your channels when it crosses a threshold.</SheetDescription>
|
|
111
|
-
</SheetHeader>
|
|
112
|
-
<SheetBody>
|
|
113
|
-
{status === "loading" && (
|
|
114
|
-
<div className="grid gap-6" aria-hidden="true">
|
|
115
|
-
{[0, 1, 2, 3].map((i) => <div key={i} className="grid gap-2"><Skeleton className="h-4 w-28" /><Skeleton className="h-9 w-full" /></div>)}
|
|
116
|
-
</div>
|
|
117
|
-
)}
|
|
118
|
-
{status === "error" && (
|
|
119
|
-
<div className="grid justify-items-center gap-4 py-8 text-center" role="alert">
|
|
120
|
-
<TriangleAlert className="size-8 text-destructive" />
|
|
121
|
-
<p className="text-h3 text-foreground">Couldn't load this rule</p>
|
|
122
|
-
<p className="max-w-sm text-small text-muted-foreground">The alerting service didn't respond, so its current settings aren't available. Retry, or close and reopen it from the alert list.</p>
|
|
123
|
-
<Button variant="outline" onClick={onRetryLoad}><RotateCcw className="size-4" /> Retry</Button>
|
|
124
|
-
</div>
|
|
125
|
-
)}
|
|
126
|
-
{status === "empty" && (
|
|
127
|
-
<EmptyState
|
|
128
|
-
icon={<Plus className="size-6 text-muted-foreground" />}
|
|
129
|
-
title="No notification channels yet"
|
|
130
|
-
description="An alert rule needs somewhere to send alerts. Add a channel, then come back to finish this rule."
|
|
131
|
-
action={<Button onClick={onAddChannel}><Plus className="size-4" /> Add a channel</Button>}
|
|
132
|
-
/>
|
|
133
|
-
)}
|
|
134
|
-
{isForm && phase === "form" && (
|
|
135
|
-
<form id="rule-form" onSubmit={handlePreview} className="grid gap-6">
|
|
136
|
-
{status === "degraded" && (
|
|
137
|
-
<Alert role="status" aria-live="polite">
|
|
138
|
-
<TriangleAlert className="size-4" /><AlertTitle>Metric list may be stale</AlertTitle>
|
|
139
|
-
<AlertDescription>The metrics service is unreachable, so this is a cached catalog from {metricsCachedAt}. A metric added since then won't appear here yet.</AlertDescription>
|
|
140
|
-
</Alert>
|
|
141
|
-
)}
|
|
142
|
-
{submitStatus === "failed" && submitError && (
|
|
143
|
-
<Alert variant="destructive" role="alert">
|
|
144
|
-
<TriangleAlert className="size-4" /><AlertTitle>Rule not created</AlertTitle>
|
|
145
|
-
<AlertDescription>{submitError} Your entries are preserved, adjust and try again.</AlertDescription>
|
|
146
|
-
</Alert>
|
|
147
|
-
)}
|
|
148
|
-
<Field id="name" label="Rule name" htmlFor="name" error={show("name")}>
|
|
149
|
-
<Input id="name" value={draft.name} placeholder="API error rate over 2 percent" onChange={(e) => patch("name", e.target.value)} onBlur={() => blur("name")} aria-invalid={show("name") ? true : undefined} aria-describedby={show("name") ? "name-error" : undefined} />
|
|
150
|
-
</Field>
|
|
151
|
-
<Field id="metric" label="Metric" htmlFor="metric" error={show("metricSlug")}>
|
|
152
|
-
<Select value={draft.metricSlug} onValueChange={(v) => { patch("metricSlug", v); blur("metricSlug"); }}>
|
|
153
|
-
<SelectTrigger id="metric" aria-invalid={show("metricSlug") ? true : undefined}><SelectValue placeholder="Choose a metric to watch" /></SelectTrigger>
|
|
154
|
-
<SelectContent>{metrics.map((m) => <SelectItem key={m.slug} value={m.slug}>{m.label}</SelectItem>)}</SelectContent>
|
|
155
|
-
</Select>
|
|
156
|
-
</Field>
|
|
157
|
-
<div className="grid grid-cols-2 gap-4">
|
|
158
|
-
<Field id="direction" label="Condition">
|
|
159
|
-
<RadioGroupSegmented value={draft.direction} aria-labelledby="direction-label" onValueChange={(v) => patch("direction", v as ThresholdDirection)}>
|
|
160
|
-
<RadioGroupSegmentedItem value="above">Above</RadioGroupSegmentedItem>
|
|
161
|
-
<RadioGroupSegmentedItem value="below">Below</RadioGroupSegmentedItem>
|
|
162
|
-
</RadioGroupSegmented>
|
|
163
|
-
</Field>
|
|
164
|
-
<Field id="threshold" label={`Threshold${metric ? ` (${metric.unit})` : ""}`} htmlFor="threshold" error={show("threshold")}>
|
|
165
|
-
<NumberInput id="threshold" className="tabular-nums" value={draft.threshold} step={0.5} allowDecimals onSave={(v) => patch("threshold", v)} onBlur={() => blur("threshold")} aria-invalid={show("threshold") ? true : undefined} aria-describedby={show("threshold") ? "threshold-error" : undefined} />
|
|
166
|
-
</Field>
|
|
167
|
-
</div>
|
|
168
|
-
<Field id="channels" label="Notify channels" error={show("channelIds")}>
|
|
169
|
-
<div className="grid gap-2" role="group" aria-labelledby="channels-label">
|
|
170
|
-
{channels.map((c) => (
|
|
171
|
-
<Label key={c.id} className="flex items-center gap-3 text-small font-normal">
|
|
172
|
-
<Checkbox checked={draft.channelIds.includes(c.id)} onCheckedChange={(v) => { patch("channelIds", v ? [...draft.channelIds, c.id] : draft.channelIds.filter((id) => id !== c.id)); blur("channelIds"); }} />
|
|
173
|
-
<span className="text-foreground">{c.label}</span>
|
|
174
|
-
<Badge variant="outline" className="ml-auto">{c.kind}</Badge>
|
|
175
|
-
</Label>
|
|
176
|
-
))}
|
|
177
|
-
</div>
|
|
178
|
-
</Field>
|
|
179
|
-
<Field id="enabled" label="Enable on save">
|
|
180
|
-
<div className="flex items-center gap-3">
|
|
181
|
-
<Switch checked={draft.enabled} aria-labelledby="enabled-label" onCheckedChange={(v) => patch("enabled", v)} />
|
|
182
|
-
<span className="text-small text-muted-foreground">{draft.enabled ? "Starts evaluating as soon as it's created." : "Saved paused, enable it later from the alert list."}</span>
|
|
183
|
-
</div>
|
|
184
|
-
</Field>
|
|
185
|
-
</form>
|
|
186
|
-
)}
|
|
187
|
-
{isForm && phase === "preview" && (
|
|
188
|
-
<div className="grid gap-4">
|
|
189
|
-
<p className="text-h3 text-foreground">Review before creating</p>
|
|
190
|
-
<dl className="grid gap-3 text-small">
|
|
191
|
-
<PreviewRow label="Rule"><span className="font-medium text-foreground">{draft.name}</span></PreviewRow>
|
|
192
|
-
<PreviewRow label="Condition">{metric?.label} goes {draft.direction} <span className="font-mono tabular-nums">{draft.threshold}</span> {metric?.unit}</PreviewRow>
|
|
193
|
-
<PreviewRow label="Channels">{selectedChannels.map((c) => c.label).join(", ")}</PreviewRow>
|
|
194
|
-
<PreviewRow label="State">{draft.enabled ? "Enabled on create" : "Created paused"}</PreviewRow>
|
|
195
|
-
</dl>
|
|
196
|
-
<p className="text-small text-muted-foreground">A rule that pages is hard to unsend, so confirm the scope before creating.</p>
|
|
197
|
-
</div>
|
|
198
|
-
)}
|
|
199
|
-
{isForm && phase === "success" && (
|
|
200
|
-
<div className="grid justify-items-center gap-4 py-8 text-center" role="status" aria-live="polite">
|
|
201
|
-
<span className="grid size-14 place-items-center rounded-full bg-surface-raised text-success shadow-md"><CircleCheck className="size-8" /></span>
|
|
202
|
-
<p className="text-h3 text-foreground">Alert rule created</p>
|
|
203
|
-
<p className="max-w-sm text-small text-muted-foreground">{draft.name} is {draft.enabled ? "live and evaluating now" : "saved and paused"}. It notifies <span className="tabular-nums">{selectedChannels.length}</span> channel{selectedChannels.length === 1 ? "" : "s"} when it fires.</p>
|
|
204
|
-
<div className="flex items-center gap-2">
|
|
205
|
-
<Button variant="outline" onClick={onCancel}>View rule</Button>
|
|
206
|
-
<Button onClick={reset}>Create another rule</Button>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
)}
|
|
210
|
-
</SheetBody>
|
|
211
|
-
{isForm && phase !== "success" && (
|
|
212
|
-
<SheetFooter showDivider>
|
|
213
|
-
<div className="flex w-full items-center justify-between gap-4">
|
|
214
|
-
<p className="text-small text-muted-foreground" role="status" aria-live="polite">
|
|
215
|
-
{busy ? "Creating rule" : phase === "form" ? <>Press <Kbd size="sm">Enter</Kbd> to review before creating.</> : "Nothing is created until you confirm."}
|
|
216
|
-
</p>
|
|
217
|
-
<div className="flex items-center gap-2">
|
|
218
|
-
{phase === "form" ? (
|
|
219
|
-
<>
|
|
220
|
-
<Button type="button" variant="ghost" onClick={onCancel}>Cancel</Button>
|
|
221
|
-
<Button type="submit" form="rule-form" size="lg">{mode === "create" ? "Create rule" : "Save changes"}</Button>
|
|
222
|
-
</>
|
|
223
|
-
) : (
|
|
224
|
-
<>
|
|
225
|
-
<Button type="button" variant="ghost" onClick={() => setPhase("form")} disabled={busy}>Back to edit</Button>
|
|
226
|
-
<Button type="button" size="lg" onClick={handleConfirm} disabled={busy}>{busy ? "Creating rule" : "Confirm and create"}</Button>
|
|
227
|
-
</>
|
|
228
|
-
)}
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
</SheetFooter>
|
|
232
|
-
)}
|
|
233
|
-
</SheetContent>
|
|
234
|
-
</Sheet>
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// exemplar fixture data — seeded, domain-shaped
|
|
239
|
-
export const metricCatalog: MetricOption[] = [
|
|
240
|
-
{ slug: "api_error_rate", label: "API error rate", unit: "%" },
|
|
241
|
-
{ slug: "p95_latency", label: "p95 response time", unit: "ms" },
|
|
242
|
-
{ slug: "queue_depth", label: "Ingest queue depth", unit: "jobs" },
|
|
243
|
-
];
|
|
244
|
-
|
|
245
|
-
export const notifyChannels: NotifyChannel[] = [
|
|
246
|
-
{ id: "ch_oncall_primary", label: "On-call primary", kind: "pager" },
|
|
247
|
-
{ id: "ch_platform_alerts", label: "#platform-alerts", kind: "webhook" },
|
|
248
|
-
{ id: "ch_sre_mailing", label: "sre-team mailing list", kind: "email" },
|
|
249
|
-
];
|