From 929bec8f310acfa06659f8965423f870febfbd84 Mon Sep 17 00:00:00 2001 From: Khalid Abdi Date: Sun, 14 Jun 2026 19:27:17 +0300 Subject: [PATCH] feat: AI-added records save with placeholders + "Added by AI" provenance Stop blocking AI imports/proposals on missing non-critical fields. Records the chat agent drafts now save with safe placeholders, auto-generated file numbers, and a source="ai" marker that surfaces an "Added by AI" badge so a clinician can review/edit them later. Backend: - add `source` (manual|ai) column to patients/appointments/prescriptions (migration 0014) + canonical types, services, validation schemas - relax patient/appointment validation: empty file number allowed, demographic + type/provider/initials fall back to placeholders (initials derived from name) - patients.generateFileNumber() auto-assigns an MRN when one is missing - proposeAppointment accepts a name when no file number resolves; AI commits + /api/ai/import stamp source="ai" Frontend: - `source` on Appointment/Patient/Prescription types; AI commits send source="ai" - reusable shown on the Patients table/detail and prescriptions list Co-Authored-By: Claude Opus 4.8 --- .agents/skills/ai-elements/SKILL.md | 160 + .../skills/ai-elements/references/agent.md | 134 + .../skills/ai-elements/references/artifact.md | 84 + .../ai-elements/references/attachments.md | 190 ++ .../ai-elements/references/audio-player.md | 134 + .../skills/ai-elements/references/canvas.md | 34 + .../references/chain-of-thought.md | 81 + .../ai-elements/references/checkpoint.md | 183 ++ .../ai-elements/references/code-block.md | 170 + .../skills/ai-elements/references/commit.md | 177 ++ .../ai-elements/references/confirmation.md | 262 ++ .../ai-elements/references/connection.md | 34 + .../skills/ai-elements/references/context.md | 126 + .../skills/ai-elements/references/controls.md | 32 + .../ai-elements/references/conversation.md | 224 ++ .agents/skills/ai-elements/references/edge.md | 52 + .../references/environment-variables.md | 102 + .../ai-elements/references/file-tree.md | 72 + .../skills/ai-elements/references/image.md | 146 + .../ai-elements/references/inline-citation.md | 299 ++ .../ai-elements/references/jsx-preview.md | 104 + .../skills/ai-elements/references/message.md | 237 ++ .../ai-elements/references/mic-selector.md | 189 ++ .../ai-elements/references/model-selector.md | 112 + .agents/skills/ai-elements/references/node.md | 73 + .../ai-elements/references/open-in-chat.md | 67 + .../ai-elements/references/package-info.md | 95 + .../skills/ai-elements/references/panel.md | 33 + .../skills/ai-elements/references/persona.md | 166 + .agents/skills/ai-elements/references/plan.md | 79 + .../ai-elements/references/prompt-input.md | 579 ++++ .../skills/ai-elements/references/queue.md | 172 + .../ai-elements/references/reasoning.md | 238 ++ .../skills/ai-elements/references/sandbox.md | 129 + .../ai-elements/references/schema-display.md | 102 + .../skills/ai-elements/references/shimmer.md | 48 + .../skills/ai-elements/references/snippet.md | 64 + .../skills/ai-elements/references/sources.md | 209 ++ .../ai-elements/references/speech-input.md | 163 + .../ai-elements/references/stack-trace.md | 224 ++ .../ai-elements/references/suggestion.md | 124 + .agents/skills/ai-elements/references/task.md | 222 ++ .../skills/ai-elements/references/terminal.md | 103 + .../ai-elements/references/test-results.md | 157 + .agents/skills/ai-elements/references/tool.md | 285 ++ .../skills/ai-elements/references/toolbar.md | 32 + .../ai-elements/references/transcription.md | 120 + .../ai-elements/references/voice-selector.md | 241 ++ .../ai-elements/references/web-preview.md | 200 ++ .agents/skills/ai-elements/scripts/agent.tsx | 61 + .../skills/ai-elements/scripts/artifact.tsx | 127 + .../scripts/attachments-inline.tsx | 128 + .../ai-elements/scripts/attachments-list.tsx | 95 + .../ai-elements/scripts/attachments.tsx | 85 + .../scripts/audio-player-remote.tsx | 35 + .../ai-elements/scripts/audio-player.tsx | 68 + .../ai-elements/scripts/chain-of-thought.tsx | 82 + .../skills/ai-elements/scripts/checkpoint.tsx | 110 + .../ai-elements/scripts/code-block-dark.tsx | 46 + .../skills/ai-elements/scripts/code-block.tsx | 120 + .agents/skills/ai-elements/scripts/commit.tsx | 94 + .../scripts/confirmation-accepted.tsx | 40 + .../scripts/confirmation-rejected.tsx | 40 + .../scripts/confirmation-request.tsx | 54 + .../ai-elements/scripts/confirmation.tsx | 55 + .../skills/ai-elements/scripts/context.tsx | 45 + .../ai-elements/scripts/conversation.tsx | 177 ++ .../scripts/environment-variables.tsx | 62 + .../ai-elements/scripts/file-tree-basic.tsx | 18 + .../scripts/file-tree-expanded.tsx | 21 + .../scripts/file-tree-selection.tsx | 24 + .../skills/ai-elements/scripts/file-tree.tsx | 42 + .agents/skills/ai-elements/scripts/image.tsx | 20 + .../ai-elements/scripts/inline-citation.tsx | 97 + .../ai-elements/scripts/jsx-preview.tsx | 104 + .../skills/ai-elements/scripts/message.tsx | 365 +++ .../ai-elements/scripts/mic-selector.tsx | 49 + .../ai-elements/scripts/model-selector.tsx | 365 +++ .../ai-elements/scripts/open-in-chat.tsx | 33 + .../ai-elements/scripts/package-info.tsx | 51 + .../ai-elements/scripts/persona-command.tsx | 110 + .../ai-elements/scripts/persona-glint.tsx | 110 + .../ai-elements/scripts/persona-halo.tsx | 110 + .../ai-elements/scripts/persona-mana.tsx | 110 + .../ai-elements/scripts/persona-obsidian.tsx | 110 + .../ai-elements/scripts/persona-opal.tsx | 110 + .agents/skills/ai-elements/scripts/plan.tsx | 65 + .../scripts/prompt-input-cursor.tsx | 502 +++ .../scripts/prompt-input-tooltip.tsx | 44 + .../ai-elements/scripts/prompt-input.tsx | 267 ++ .../scripts/queue-prompt-input.tsx | 394 +++ .agents/skills/ai-elements/scripts/queue.tsx | 295 ++ .../skills/ai-elements/scripts/reasoning.tsx | 71 + .../skills/ai-elements/scripts/sandbox.tsx | 178 ++ .../scripts/schema-display-basic.tsx | 9 + .../scripts/schema-display-body.tsx | 20 + .../scripts/schema-display-nested.tsx | 23 + .../scripts/schema-display-params.tsx | 16 + .../ai-elements/scripts/schema-display.tsx | 110 + .../ai-elements/scripts/shimmer-duration.tsx | 31 + .../ai-elements/scripts/shimmer-elements.tsx | 43 + .../skills/ai-elements/scripts/shimmer.tsx | 17 + .../ai-elements/scripts/snippet-plain.tsx | 19 + .../skills/ai-elements/scripts/snippet.tsx | 25 + .../ai-elements/scripts/sources-custom.tsx | 39 + .../skills/ai-elements/scripts/sources.tsx | 32 + .../ai-elements/scripts/speech-input.tsx | 84 + .../scripts/stack-trace-collapsed.tsx | 39 + .../scripts/stack-trace-no-internal.tsx | 41 + .../ai-elements/scripts/stack-trace.tsx | 58 + .../ai-elements/scripts/suggestion-input.tsx | 144 + .../skills/ai-elements/scripts/suggestion.tsx | 32 + .agents/skills/ai-elements/scripts/task.tsx | 59 + .../ai-elements/scripts/terminal-basic.tsx | 7 + .../ai-elements/scripts/terminal-clear.tsx | 19 + .../scripts/terminal-streaming.tsx | 38 + .../skills/ai-elements/scripts/terminal.tsx | 82 + .../scripts/test-results-basic.tsx | 27 + .../scripts/test-results-errors.tsx | 51 + .../scripts/test-results-suites.tsx | 40 + .../ai-elements/scripts/test-results.tsx | 102 + .../scripts/tool-input-available.tsx | 31 + .../scripts/tool-input-streaming.tsx | 30 + .../scripts/tool-output-available.tsx | 80 + .../ai-elements/scripts/tool-output-error.tsx | 44 + .agents/skills/ai-elements/scripts/tool.tsx | 222 ++ .../ai-elements/scripts/transcription.tsx | 290 ++ .../ai-elements/scripts/voice-selector.tsx | 245 ++ .../ai-elements/scripts/web-preview.tsx | 115 + .../better-auth-best-practices/SKILL.md | 175 ++ .../SKILL.MD | 432 +++ .agents/skills/bklit-ui/SKILL.md | 136 + .agents/skills/bklit-ui/rules/animation.md | 42 + .agents/skills/bklit-ui/rules/composition.md | 57 + .agents/skills/bklit-ui/rules/installation.md | 74 + .agents/skills/bklit-ui/rules/theming.md | 55 + .agents/skills/bklit-ui/rules/tooltips.md | 71 + .agents/skills/coss-particles/SKILL.md | 737 +++++ .agents/skills/coss/SKILL.md | 117 + .agents/skills/coss/references/cli.md | 110 + .../coss/references/component-registry.md | 74 + .../skills/coss/references/portal-props.md | 21 + .../coss/references/primitives/accordion.md | 92 + .../references/primitives/alert-dialog.md | 85 + .../coss/references/primitives/alert.md | 90 + .../references/primitives/autocomplete.md | 134 + .../coss/references/primitives/avatar.md | 78 + .../coss/references/primitives/badge.md | 79 + .../coss/references/primitives/breadcrumb.md | 91 + .../coss/references/primitives/button.md | 109 + .../coss/references/primitives/calendar.md | 68 + .../skills/coss/references/primitives/card.md | 85 + .../references/primitives/checkbox-group.md | 95 + .../coss/references/primitives/checkbox.md | 95 + .../coss/references/primitives/collapsible.md | 70 + .../coss/references/primitives/combobox.md | 114 + .../coss/references/primitives/command.md | 121 + .../coss/references/primitives/dialog.md | 93 + .../coss/references/primitives/drawer.md | 121 + .../coss/references/primitives/empty.md | 88 + .../coss/references/primitives/field.md | 101 + .../coss/references/primitives/fieldset.md | 69 + .../skills/coss/references/primitives/form.md | 79 + .../coss/references/primitives/frame.md | 82 + .../coss/references/primitives/group.md | 84 + .../coss/references/primitives/input-group.md | 104 + .../coss/references/primitives/input.md | 80 + .../skills/coss/references/primitives/kbd.md | 78 + .../coss/references/primitives/label.md | 71 + .../skills/coss/references/primitives/menu.md | 97 + .../coss/references/primitives/meter.md | 75 + .../references/primitives/number-field.md | 102 + .../coss/references/primitives/otp-field.md | 94 + .../coss/references/primitives/pagination.md | 98 + .../coss/references/primitives/popover.md | 75 + .../references/primitives/preview-card.md | 80 + .../coss/references/primitives/progress.md | 72 + .../coss/references/primitives/radio-group.md | 102 + .../coss/references/primitives/scroll-area.md | 101 + .../coss/references/primitives/select.md | 101 + .../coss/references/primitives/separator.md | 69 + .../coss/references/primitives/sheet.md | 109 + .../coss/references/primitives/sidebar.md | 135 + .../coss/references/primitives/skeleton.md | 65 + .../coss/references/primitives/slider.md | 69 + .../coss/references/primitives/spinner.md | 73 + .../coss/references/primitives/switch.md | 86 + .../coss/references/primitives/table.md | 81 + .../skills/coss/references/primitives/tabs.md | 86 + .../coss/references/primitives/textarea.md | 71 + .../coss/references/primitives/toast.md | 129 + .../references/primitives/toggle-group.md | 88 + .../coss/references/primitives/toggle.md | 83 + .../coss/references/primitives/toolbar.md | 73 + .../coss/references/primitives/tooltip.md | 95 + .../coss/references/rules/composition.md | 46 + .agents/skills/coss/references/rules/forms.md | 52 + .../skills/coss/references/rules/migration.md | 173 + .../skills/coss/references/rules/styling.md | 98 + .agents/skills/create-auth-skill/SKILL.md | 321 ++ .../SKILL.md | 212 ++ .../organization-best-practices/SKILL.md | 479 +++ .agents/skills/shadcn/SKILL.md | 267 ++ .agents/skills/shadcn/agents/openai.yml | 5 + .agents/skills/shadcn/assets/shadcn-small.png | Bin 0 -> 1049 bytes .agents/skills/shadcn/assets/shadcn.png | Bin 0 -> 3852 bytes .agents/skills/shadcn/cli.md | 290 ++ .agents/skills/shadcn/customization.md | 209 ++ .agents/skills/shadcn/evals/evals.json | 47 + .agents/skills/shadcn/mcp.md | 105 + .agents/skills/shadcn/registry.md | 277 ++ .agents/skills/shadcn/rules/base-vs-radix.md | 306 ++ .agents/skills/shadcn/rules/composition.md | 195 ++ .agents/skills/shadcn/rules/forms.md | 192 ++ .agents/skills/shadcn/rules/icons.md | 101 + .agents/skills/shadcn/rules/styling.md | 162 + .../SKILL.md | 331 ++ .claude/skills/ai-elements | 1 + .claude/skills/better-auth-best-practices | 1 + .../better-auth-security-best-practices | 1 + .claude/skills/bklit-ui | 1 + .claude/skills/coss | 1 + .claude/skills/coss-particles | 1 + .claude/skills/create-auth-skill | 1 + .../skills/email-and-password-best-practices | 1 + .claude/skills/organization-best-practices | 1 + .claude/skills/shadcn | 1 + .../two-factor-authentication-best-practices | 1 + backend/drizzle/0014_youthful_aqueduct.sql | 3 + backend/drizzle/meta/0014_snapshot.json | 2798 +++++++++++++++++ backend/drizzle/meta/_journal.json | 7 + backend/src/db/schema/appointments.ts | 3 + backend/src/db/schema/patients.ts | 4 + backend/src/db/schema/prescriptions.ts | 2 + backend/src/lib/appointment-validation.ts | 41 +- backend/src/lib/initials.ts | 10 + backend/src/lib/patient-validation.ts | 67 +- backend/src/lib/prescription-validation.ts | 1 + backend/src/routes/ai.ts | 2 +- backend/src/services/ai/tools.ts | 40 +- backend/src/services/appointments.ts | 2 + backend/src/services/patients.ts | 27 +- backend/src/services/prescriptions.ts | 2 + backend/src/types/appointment.ts | 1 + backend/src/types/patient.ts | 1 + backend/src/types/prescription.ts | 1 + frontend/components/ai-badge.tsx | 27 + .../components/chat/action-preview-card.tsx | 12 +- .../patients/patient-detail-sheet.tsx | 6 +- .../components/patients/patients-view.tsx | 6 +- .../prescriptions/prescriptions-view.tsx | 2 + frontend/lib/appointments.ts | 2 + frontend/lib/i18n/locales/en/translation.json | 3 +- frontend/lib/patients.ts | 1 + frontend/lib/prescriptions.ts | 2 + package-lock.json | 627 +--- package.json | 2 +- skills-lock.json | 8 +- 258 files changed, 28490 insertions(+), 681 deletions(-) create mode 100644 .agents/skills/ai-elements/SKILL.md create mode 100644 .agents/skills/ai-elements/references/agent.md create mode 100644 .agents/skills/ai-elements/references/artifact.md create mode 100644 .agents/skills/ai-elements/references/attachments.md create mode 100644 .agents/skills/ai-elements/references/audio-player.md create mode 100644 .agents/skills/ai-elements/references/canvas.md create mode 100644 .agents/skills/ai-elements/references/chain-of-thought.md create mode 100644 .agents/skills/ai-elements/references/checkpoint.md create mode 100644 .agents/skills/ai-elements/references/code-block.md create mode 100644 .agents/skills/ai-elements/references/commit.md create mode 100644 .agents/skills/ai-elements/references/confirmation.md create mode 100644 .agents/skills/ai-elements/references/connection.md create mode 100644 .agents/skills/ai-elements/references/context.md create mode 100644 .agents/skills/ai-elements/references/controls.md create mode 100644 .agents/skills/ai-elements/references/conversation.md create mode 100644 .agents/skills/ai-elements/references/edge.md create mode 100644 .agents/skills/ai-elements/references/environment-variables.md create mode 100644 .agents/skills/ai-elements/references/file-tree.md create mode 100644 .agents/skills/ai-elements/references/image.md create mode 100644 .agents/skills/ai-elements/references/inline-citation.md create mode 100644 .agents/skills/ai-elements/references/jsx-preview.md create mode 100644 .agents/skills/ai-elements/references/message.md create mode 100644 .agents/skills/ai-elements/references/mic-selector.md create mode 100644 .agents/skills/ai-elements/references/model-selector.md create mode 100644 .agents/skills/ai-elements/references/node.md create mode 100644 .agents/skills/ai-elements/references/open-in-chat.md create mode 100644 .agents/skills/ai-elements/references/package-info.md create mode 100644 .agents/skills/ai-elements/references/panel.md create mode 100644 .agents/skills/ai-elements/references/persona.md create mode 100644 .agents/skills/ai-elements/references/plan.md create mode 100644 .agents/skills/ai-elements/references/prompt-input.md create mode 100644 .agents/skills/ai-elements/references/queue.md create mode 100644 .agents/skills/ai-elements/references/reasoning.md create mode 100644 .agents/skills/ai-elements/references/sandbox.md create mode 100644 .agents/skills/ai-elements/references/schema-display.md create mode 100644 .agents/skills/ai-elements/references/shimmer.md create mode 100644 .agents/skills/ai-elements/references/snippet.md create mode 100644 .agents/skills/ai-elements/references/sources.md create mode 100644 .agents/skills/ai-elements/references/speech-input.md create mode 100644 .agents/skills/ai-elements/references/stack-trace.md create mode 100644 .agents/skills/ai-elements/references/suggestion.md create mode 100644 .agents/skills/ai-elements/references/task.md create mode 100644 .agents/skills/ai-elements/references/terminal.md create mode 100644 .agents/skills/ai-elements/references/test-results.md create mode 100644 .agents/skills/ai-elements/references/tool.md create mode 100644 .agents/skills/ai-elements/references/toolbar.md create mode 100644 .agents/skills/ai-elements/references/transcription.md create mode 100644 .agents/skills/ai-elements/references/voice-selector.md create mode 100644 .agents/skills/ai-elements/references/web-preview.md create mode 100644 .agents/skills/ai-elements/scripts/agent.tsx create mode 100644 .agents/skills/ai-elements/scripts/artifact.tsx create mode 100644 .agents/skills/ai-elements/scripts/attachments-inline.tsx create mode 100644 .agents/skills/ai-elements/scripts/attachments-list.tsx create mode 100644 .agents/skills/ai-elements/scripts/attachments.tsx create mode 100644 .agents/skills/ai-elements/scripts/audio-player-remote.tsx create mode 100644 .agents/skills/ai-elements/scripts/audio-player.tsx create mode 100644 .agents/skills/ai-elements/scripts/chain-of-thought.tsx create mode 100644 .agents/skills/ai-elements/scripts/checkpoint.tsx create mode 100644 .agents/skills/ai-elements/scripts/code-block-dark.tsx create mode 100644 .agents/skills/ai-elements/scripts/code-block.tsx create mode 100644 .agents/skills/ai-elements/scripts/commit.tsx create mode 100644 .agents/skills/ai-elements/scripts/confirmation-accepted.tsx create mode 100644 .agents/skills/ai-elements/scripts/confirmation-rejected.tsx create mode 100644 .agents/skills/ai-elements/scripts/confirmation-request.tsx create mode 100644 .agents/skills/ai-elements/scripts/confirmation.tsx create mode 100644 .agents/skills/ai-elements/scripts/context.tsx create mode 100644 .agents/skills/ai-elements/scripts/conversation.tsx create mode 100644 .agents/skills/ai-elements/scripts/environment-variables.tsx create mode 100644 .agents/skills/ai-elements/scripts/file-tree-basic.tsx create mode 100644 .agents/skills/ai-elements/scripts/file-tree-expanded.tsx create mode 100644 .agents/skills/ai-elements/scripts/file-tree-selection.tsx create mode 100644 .agents/skills/ai-elements/scripts/file-tree.tsx create mode 100644 .agents/skills/ai-elements/scripts/image.tsx create mode 100644 .agents/skills/ai-elements/scripts/inline-citation.tsx create mode 100644 .agents/skills/ai-elements/scripts/jsx-preview.tsx create mode 100644 .agents/skills/ai-elements/scripts/message.tsx create mode 100644 .agents/skills/ai-elements/scripts/mic-selector.tsx create mode 100644 .agents/skills/ai-elements/scripts/model-selector.tsx create mode 100644 .agents/skills/ai-elements/scripts/open-in-chat.tsx create mode 100644 .agents/skills/ai-elements/scripts/package-info.tsx create mode 100644 .agents/skills/ai-elements/scripts/persona-command.tsx create mode 100644 .agents/skills/ai-elements/scripts/persona-glint.tsx create mode 100644 .agents/skills/ai-elements/scripts/persona-halo.tsx create mode 100644 .agents/skills/ai-elements/scripts/persona-mana.tsx create mode 100644 .agents/skills/ai-elements/scripts/persona-obsidian.tsx create mode 100644 .agents/skills/ai-elements/scripts/persona-opal.tsx create mode 100644 .agents/skills/ai-elements/scripts/plan.tsx create mode 100644 .agents/skills/ai-elements/scripts/prompt-input-cursor.tsx create mode 100644 .agents/skills/ai-elements/scripts/prompt-input-tooltip.tsx create mode 100644 .agents/skills/ai-elements/scripts/prompt-input.tsx create mode 100644 .agents/skills/ai-elements/scripts/queue-prompt-input.tsx create mode 100644 .agents/skills/ai-elements/scripts/queue.tsx create mode 100644 .agents/skills/ai-elements/scripts/reasoning.tsx create mode 100644 .agents/skills/ai-elements/scripts/sandbox.tsx create mode 100644 .agents/skills/ai-elements/scripts/schema-display-basic.tsx create mode 100644 .agents/skills/ai-elements/scripts/schema-display-body.tsx create mode 100644 .agents/skills/ai-elements/scripts/schema-display-nested.tsx create mode 100644 .agents/skills/ai-elements/scripts/schema-display-params.tsx create mode 100644 .agents/skills/ai-elements/scripts/schema-display.tsx create mode 100644 .agents/skills/ai-elements/scripts/shimmer-duration.tsx create mode 100644 .agents/skills/ai-elements/scripts/shimmer-elements.tsx create mode 100644 .agents/skills/ai-elements/scripts/shimmer.tsx create mode 100644 .agents/skills/ai-elements/scripts/snippet-plain.tsx create mode 100644 .agents/skills/ai-elements/scripts/snippet.tsx create mode 100644 .agents/skills/ai-elements/scripts/sources-custom.tsx create mode 100644 .agents/skills/ai-elements/scripts/sources.tsx create mode 100644 .agents/skills/ai-elements/scripts/speech-input.tsx create mode 100644 .agents/skills/ai-elements/scripts/stack-trace-collapsed.tsx create mode 100644 .agents/skills/ai-elements/scripts/stack-trace-no-internal.tsx create mode 100644 .agents/skills/ai-elements/scripts/stack-trace.tsx create mode 100644 .agents/skills/ai-elements/scripts/suggestion-input.tsx create mode 100644 .agents/skills/ai-elements/scripts/suggestion.tsx create mode 100644 .agents/skills/ai-elements/scripts/task.tsx create mode 100644 .agents/skills/ai-elements/scripts/terminal-basic.tsx create mode 100644 .agents/skills/ai-elements/scripts/terminal-clear.tsx create mode 100644 .agents/skills/ai-elements/scripts/terminal-streaming.tsx create mode 100644 .agents/skills/ai-elements/scripts/terminal.tsx create mode 100644 .agents/skills/ai-elements/scripts/test-results-basic.tsx create mode 100644 .agents/skills/ai-elements/scripts/test-results-errors.tsx create mode 100644 .agents/skills/ai-elements/scripts/test-results-suites.tsx create mode 100644 .agents/skills/ai-elements/scripts/test-results.tsx create mode 100644 .agents/skills/ai-elements/scripts/tool-input-available.tsx create mode 100644 .agents/skills/ai-elements/scripts/tool-input-streaming.tsx create mode 100644 .agents/skills/ai-elements/scripts/tool-output-available.tsx create mode 100644 .agents/skills/ai-elements/scripts/tool-output-error.tsx create mode 100644 .agents/skills/ai-elements/scripts/tool.tsx create mode 100644 .agents/skills/ai-elements/scripts/transcription.tsx create mode 100644 .agents/skills/ai-elements/scripts/voice-selector.tsx create mode 100644 .agents/skills/ai-elements/scripts/web-preview.tsx create mode 100644 .agents/skills/better-auth-best-practices/SKILL.md create mode 100644 .agents/skills/better-auth-security-best-practices/SKILL.MD create mode 100644 .agents/skills/bklit-ui/SKILL.md create mode 100644 .agents/skills/bklit-ui/rules/animation.md create mode 100644 .agents/skills/bklit-ui/rules/composition.md create mode 100644 .agents/skills/bklit-ui/rules/installation.md create mode 100644 .agents/skills/bklit-ui/rules/theming.md create mode 100644 .agents/skills/bklit-ui/rules/tooltips.md create mode 100644 .agents/skills/coss-particles/SKILL.md create mode 100644 .agents/skills/coss/SKILL.md create mode 100644 .agents/skills/coss/references/cli.md create mode 100644 .agents/skills/coss/references/component-registry.md create mode 100644 .agents/skills/coss/references/portal-props.md create mode 100644 .agents/skills/coss/references/primitives/accordion.md create mode 100644 .agents/skills/coss/references/primitives/alert-dialog.md create mode 100644 .agents/skills/coss/references/primitives/alert.md create mode 100644 .agents/skills/coss/references/primitives/autocomplete.md create mode 100644 .agents/skills/coss/references/primitives/avatar.md create mode 100644 .agents/skills/coss/references/primitives/badge.md create mode 100644 .agents/skills/coss/references/primitives/breadcrumb.md create mode 100644 .agents/skills/coss/references/primitives/button.md create mode 100644 .agents/skills/coss/references/primitives/calendar.md create mode 100644 .agents/skills/coss/references/primitives/card.md create mode 100644 .agents/skills/coss/references/primitives/checkbox-group.md create mode 100644 .agents/skills/coss/references/primitives/checkbox.md create mode 100644 .agents/skills/coss/references/primitives/collapsible.md create mode 100644 .agents/skills/coss/references/primitives/combobox.md create mode 100644 .agents/skills/coss/references/primitives/command.md create mode 100644 .agents/skills/coss/references/primitives/dialog.md create mode 100644 .agents/skills/coss/references/primitives/drawer.md create mode 100644 .agents/skills/coss/references/primitives/empty.md create mode 100644 .agents/skills/coss/references/primitives/field.md create mode 100644 .agents/skills/coss/references/primitives/fieldset.md create mode 100644 .agents/skills/coss/references/primitives/form.md create mode 100644 .agents/skills/coss/references/primitives/frame.md create mode 100644 .agents/skills/coss/references/primitives/group.md create mode 100644 .agents/skills/coss/references/primitives/input-group.md create mode 100644 .agents/skills/coss/references/primitives/input.md create mode 100644 .agents/skills/coss/references/primitives/kbd.md create mode 100644 .agents/skills/coss/references/primitives/label.md create mode 100644 .agents/skills/coss/references/primitives/menu.md create mode 100644 .agents/skills/coss/references/primitives/meter.md create mode 100644 .agents/skills/coss/references/primitives/number-field.md create mode 100644 .agents/skills/coss/references/primitives/otp-field.md create mode 100644 .agents/skills/coss/references/primitives/pagination.md create mode 100644 .agents/skills/coss/references/primitives/popover.md create mode 100644 .agents/skills/coss/references/primitives/preview-card.md create mode 100644 .agents/skills/coss/references/primitives/progress.md create mode 100644 .agents/skills/coss/references/primitives/radio-group.md create mode 100644 .agents/skills/coss/references/primitives/scroll-area.md create mode 100644 .agents/skills/coss/references/primitives/select.md create mode 100644 .agents/skills/coss/references/primitives/separator.md create mode 100644 .agents/skills/coss/references/primitives/sheet.md create mode 100644 .agents/skills/coss/references/primitives/sidebar.md create mode 100644 .agents/skills/coss/references/primitives/skeleton.md create mode 100644 .agents/skills/coss/references/primitives/slider.md create mode 100644 .agents/skills/coss/references/primitives/spinner.md create mode 100644 .agents/skills/coss/references/primitives/switch.md create mode 100644 .agents/skills/coss/references/primitives/table.md create mode 100644 .agents/skills/coss/references/primitives/tabs.md create mode 100644 .agents/skills/coss/references/primitives/textarea.md create mode 100644 .agents/skills/coss/references/primitives/toast.md create mode 100644 .agents/skills/coss/references/primitives/toggle-group.md create mode 100644 .agents/skills/coss/references/primitives/toggle.md create mode 100644 .agents/skills/coss/references/primitives/toolbar.md create mode 100644 .agents/skills/coss/references/primitives/tooltip.md create mode 100644 .agents/skills/coss/references/rules/composition.md create mode 100644 .agents/skills/coss/references/rules/forms.md create mode 100644 .agents/skills/coss/references/rules/migration.md create mode 100644 .agents/skills/coss/references/rules/styling.md create mode 100644 .agents/skills/create-auth-skill/SKILL.md create mode 100644 .agents/skills/email-and-password-best-practices/SKILL.md create mode 100644 .agents/skills/organization-best-practices/SKILL.md create mode 100644 .agents/skills/shadcn/SKILL.md create mode 100644 .agents/skills/shadcn/agents/openai.yml create mode 100644 .agents/skills/shadcn/assets/shadcn-small.png create mode 100644 .agents/skills/shadcn/assets/shadcn.png create mode 100644 .agents/skills/shadcn/cli.md create mode 100644 .agents/skills/shadcn/customization.md create mode 100644 .agents/skills/shadcn/evals/evals.json create mode 100644 .agents/skills/shadcn/mcp.md create mode 100644 .agents/skills/shadcn/registry.md create mode 100644 .agents/skills/shadcn/rules/base-vs-radix.md create mode 100644 .agents/skills/shadcn/rules/composition.md create mode 100644 .agents/skills/shadcn/rules/forms.md create mode 100644 .agents/skills/shadcn/rules/icons.md create mode 100644 .agents/skills/shadcn/rules/styling.md create mode 100644 .agents/skills/two-factor-authentication-best-practices/SKILL.md create mode 120000 .claude/skills/ai-elements create mode 120000 .claude/skills/better-auth-best-practices create mode 120000 .claude/skills/better-auth-security-best-practices create mode 120000 .claude/skills/bklit-ui create mode 120000 .claude/skills/coss create mode 120000 .claude/skills/coss-particles create mode 120000 .claude/skills/create-auth-skill create mode 120000 .claude/skills/email-and-password-best-practices create mode 120000 .claude/skills/organization-best-practices create mode 120000 .claude/skills/shadcn create mode 120000 .claude/skills/two-factor-authentication-best-practices create mode 100644 backend/drizzle/0014_youthful_aqueduct.sql create mode 100644 backend/drizzle/meta/0014_snapshot.json create mode 100644 backend/src/lib/initials.ts create mode 100644 frontend/components/ai-badge.tsx diff --git a/.agents/skills/ai-elements/SKILL.md b/.agents/skills/ai-elements/SKILL.md new file mode 100644 index 0000000..5a82537 --- /dev/null +++ b/.agents/skills/ai-elements/SKILL.md @@ -0,0 +1,160 @@ +--- +name: ai-elements +description: Build AI chat interfaces using ai-elements components — conversations, messages, tool displays, prompt inputs, and more. Use when the user wants to build a chatbot, AI assistant UI, or any AI-powered chat interface. +--- + +# AI Elements + +[AI Elements](https://www.npmjs.com/package/ai-elements) is a component library and custom registry built on top of [shadcn/ui](https://ui.shadcn.com/) to help you build AI-native applications faster. It provides pre-built components like conversations, messages and more. + +Installing AI Elements is straightforward and can be done in a couple of ways. You can use the dedicated CLI command for the fastest setup, or integrate via the standard shadcn/ui CLI if you've already adopted shadcn's workflow. + +> **IMPORTANT:** Run all CLI commands using the project's package runner: `npx ai-elements@latest`, `pnpm dlx ai-elements@latest`, or `bunx --bun ai-elements@latest` — based on the project's `packageManager`. Examples below use `npx ai-elements@latest` but substitute the correct runner for the project. + +## Prerequisites + +Before installing AI Elements, make sure your environment meets the following requirements: + +- [Node.js](https://nodejs.org/en/download/), version 18 or later +- A [Next.js](https://nextjs.org/) project with the [AI SDK](https://ai-sdk.dev/) installed. +- [shadcn/ui](https://ui.shadcn.com/) installed in your project. If you don't have it installed, running any install command will automatically install it for you. +- We also highly recommend using the [AI Gateway](https://vercel.com/docs/ai-gateway) and adding `AI_GATEWAY_API_KEY` to your `env.local` so you don't have to use an API key from every provider. AI Gateway also gives $5 in usage per month so you can experiment with models. You can obtain an API key [here](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys&title=Get%20your%20AI%20Gateway%20key). + +## Installing Components + +You can install AI Elements components using either the AI Elements CLI or the shadcn/ui CLI. Both achieve the same result: adding the selected component’s code and any needed dependencies to your project. + +The CLI will download the component’s code and integrate it into your project’s directory (usually under your components folder). By default, AI Elements components are added to the `@/components/ai-elements/` directory (or whatever folder you’ve configured in your shadcn components settings). + +After running the command, you should see a confirmation in your terminal that the files were added. You can then proceed to use the component in your code. + +## Usage + +Once an AI Elements component is installed, you can import it and use it in your application like any other React component. The components are added as part of your codebase (not hidden in a library), so the usage feels very natural. + +## Example + +After installing AI Elements components, you can use them in your application like any other React component. For example: + +```tsx title="conversation.tsx" +"use client"; + +import { + Message, + MessageContent, + MessageResponse, +} from "@/components/ai-elements/message"; +import { useChat } from "@ai-sdk/react"; + +const Example = () => { + const { messages } = useChat(); + + return ( + <> + {messages.map(({ role, parts }, index) => ( + + + {parts.map((part, i) => { + switch (part.type) { + case "text": + return ( + + {part.text} + + ); + } + })} + + + ))} + + ); +}; + +export default Example; +``` + +In the example above, we import the `Message` component from our AI Elements directory and include it in our JSX. Then, we compose the component with the `MessageContent` and `MessageResponse` subcomponents. You can style or configure the component just as you would if you wrote it yourself – since the code lives in your project, you can even open the component file to see how it works or make custom modifications. + +## Extensibility + +All AI Elements components take as many primitive attributes as possible. For example, the `Message` component extends `HTMLAttributes`, so you can pass any props that a `div` supports. This makes it easy to extend the component with your own styles or functionality. + +## Customization + +After installation, no additional setup is needed. The component’s styles (Tailwind CSS classes) and scripts are already integrated. You can start interacting with the component in your app immediately. + +For example, if you'd like to remove the rounding on `Message`, you can go to `components/ai-elements/message.tsx` and remove `rounded-lg` as follows: + +```tsx title="components/ai-elements/message.tsx" highlight="8" +export const MessageContent = ({ + children, + className, + ...props +}: MessageContentProps) => ( +
+
{children}
+
+); +``` + +## Troubleshooting + +### Why are my components not styled? + +Make sure your project is configured correctly for shadcn/ui in Tailwind 4 - this means having a `globals.css` file that imports Tailwind and includes the shadcn/ui base styles. + +### I ran the AI Elements CLI but nothing was added to my project + +Double-check that: + +- Your current working directory is the root of your project (where `package.json` lives). +- Your components.json file (if using shadcn-style config) is set up correctly. +- You’re using the latest version of the AI Elements CLI: + +```bash title="Terminal" +npx ai-elements@latest +``` + +If all else fails, feel free to open an [issue on GitHub](https://github.com/vercel/ai-elements/issues). + +### Theme switching doesn’t work — my app stays in light mode + +Ensure your app is using the same data-theme system that shadcn/ui and AI Elements expect. The default implementation toggles a data-theme attribute on the `` element. Make sure your tailwind.config.js is using class or data- selectors accordingly. + +### The component imports fail with “module not found” + +Check the file exists. If it does, make sure your `tsconfig.json` has a proper paths alias for `@/` i.e. + +```json title="tsconfig.json" +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } + } +} +``` + +### My AI coding assistant can't access AI Elements components + +1. Verify your config file syntax is valid JSON. +2. Check that the file path is correct for your AI tool. +3. Restart your coding assistant after making changes. +4. Ensure you have a stable internet connection. + +### Still stuck? + +If none of these answers help, open an [issue on GitHub](https://github.com/vercel/ai-elements/issues) and someone will be happy to assist. + +## Available Components + +See the `references/` folder for detailed documentation on each component. diff --git a/.agents/skills/ai-elements/references/agent.md b/.agents/skills/ai-elements/references/agent.md new file mode 100644 index 0000000..c2e09f9 --- /dev/null +++ b/.agents/skills/ai-elements/references/agent.md @@ -0,0 +1,134 @@ +# Agent + +A composable component for displaying AI agent configuration with model, instructions, tools, and output schema. + +The `Agent` component displays an interface for showing AI agent configuration details. It's designed to represent a configured agent from the AI SDK, showing the agent's model, system instructions, available tools (with expandable input schemas), and output schema. + +See `scripts/agent.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add agent +``` + +## Usage with AI SDK + +Display an agent's configuration alongside your chat interface. Tools are displayed in an accordion where clicking the description expands to show the input schema. + +```tsx title="app/page.tsx" +"use client"; + +import { tool } from "ai"; +import { z } from "zod"; +import { + Agent, + AgentContent, + AgentHeader, + AgentInstructions, + AgentOutput, + AgentTool, + AgentTools, +} from "@/components/ai-elements/agent"; + +const webSearch = tool({ + description: "Search the web for information", + inputSchema: z.object({ + query: z.string().describe("The search query"), + }), +}); + +const readUrl = tool({ + description: "Read and parse content from a URL", + inputSchema: z.object({ + url: z.string().url().describe("The URL to read"), + }), +}); + +const outputSchema = `z.object({ + sentiment: z.enum(['positive', 'negative', 'neutral']), + score: z.number(), + summary: z.string(), +})`; + +export default function Page() { + return ( + + + + + Analyze the sentiment of the provided text and return a structured + analysis with sentiment classification, confidence score, and summary. + + + + + + + + + ); +} +``` + +## Features + +- Model badge in header +- Instructions rendered as markdown +- Tools displayed as accordion items with expandable input schemas +- Output schema display with syntax highlighting +- Composable structure for flexible layouts +- Works with AI SDK `Tool` type + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `name` | `string` | Required | The name of the agent. | +| `model` | `string` | - | The model identifier (e.g. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `string` | Required | The instruction text. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Accordion component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `tool` | `Tool` | Required | The tool object from the AI SDK containing description and inputSchema. | +| `value` | `string` | Required | Unique identifier for the accordion item. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the AccordionItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `schema` | `string` | Required | The output schema as a string (displayed with syntax highlighting). | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the container div. | diff --git a/.agents/skills/ai-elements/references/artifact.md b/.agents/skills/ai-elements/references/artifact.md new file mode 100644 index 0000000..f8dc39a --- /dev/null +++ b/.agents/skills/ai-elements/references/artifact.md @@ -0,0 +1,84 @@ +# Artifact + +A container component for displaying generated content like code, documents, or other outputs with built-in actions. + +The `Artifact` component provides a structured container for displaying generated content like code, documents, or other outputs with built-in header actions. + +See `scripts/artifact.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add artifact +``` + +## Features + +- Structured container with header and content areas +- Built-in header with title and description support +- Flexible action buttons with tooltips +- Customizable styling for all subcomponents +- Support for close buttons and action groups +- Clean, modern design with border and shadow +- Responsive layout that adapts to content +- TypeScript support with proper type definitions +- Composable architecture for maximum flexibility + +## Examples + +### With Code Display + +See `scripts/artifact.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying paragraph element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying paragraph element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `tooltip` | `string` | - | Tooltip text to display on hover. | +| `label` | `string` | - | Screen reader label for the action button. | +| `icon` | `LucideIcon` | - | Lucide icon component to display in the button. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying div element. | diff --git a/.agents/skills/ai-elements/references/attachments.md b/.agents/skills/ai-elements/references/attachments.md new file mode 100644 index 0000000..213e248 --- /dev/null +++ b/.agents/skills/ai-elements/references/attachments.md @@ -0,0 +1,190 @@ +# Attachments + +A flexible, composable attachment component for displaying files, images, videos, audio, and source documents. + +The `Attachment` component provides a unified way to display file attachments and source documents with multiple layout variants. + +See `scripts/attachments.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add attachments +``` + +## Usage with AI SDK + +Display user-uploaded files in chat messages or input areas. + +```tsx title="app/page.tsx" +"use client"; + +import { + Attachments, + Attachment, + AttachmentPreview, + AttachmentInfo, + AttachmentRemove, +} from "@/components/ai-elements/attachments"; +import type { FileUIPart } from "ai"; + +interface MessageProps { + attachments: (FileUIPart & { id: string })[]; + onRemove?: (id: string) => void; +} + +const MessageAttachments = ({ attachments, onRemove }: MessageProps) => ( + + {attachments.map((file) => ( + onRemove(file.id) : undefined} + > + + + + ))} + +); + +export default MessageAttachments; +``` + +## Features + +- Three display variants: grid (thumbnails), inline (badges), and list (rows) +- Supports both FileUIPart and SourceDocumentUIPart from the AI SDK +- Automatic media type detection (image, video, audio, document, source) +- Hover card support for inline previews +- Remove button with customizable callback +- Composable architecture for maximum flexibility +- Accessible with proper ARIA labels +- TypeScript support with exported utility functions + +## Examples + +### Grid Variant + +Best for displaying attachments in messages with visual thumbnails. + +See `scripts/attachments.tsx` for this example. + +### Inline Variant + +Best for compact badge-style display in input areas with hover previews. + +See `scripts/attachments-inline.tsx` for this example. + +### List Variant + +Best for file lists with full metadata display. + +See `scripts/attachments-list.tsx` for this example. + +## Props + +### `` + +Container component that sets the layout variant. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `variant` | `unknown` | - | The display layout variant. | +| `...props` | `React.HTMLAttributes` | - | Spread to the underlying div element. | + +### `` + +Individual attachment item wrapper. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `data` | `unknown` | - | The attachment data (FileUIPart or SourceDocumentUIPart with id). | +| `onRemove` | `() => void` | - | Callback fired when the remove button is clicked. | +| `...props` | `React.HTMLAttributes` | - | Spread to the underlying div element. | + +### `` + +Displays the media preview (image, video, or icon). + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `fallbackIcon` | `React.ReactNode` | - | Custom icon to display when no preview is available. | +| `...props` | `React.HTMLAttributes` | - | Spread to the underlying div element. | + +### `` + +Displays the filename and optional media type. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `showMediaType` | `boolean` | `false` | Whether to show the media type below the filename. | +| `...props` | `React.HTMLAttributes` | - | Spread to the underlying div element. | + +### `` + +Remove button that appears on hover. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `label` | `string` | - | Screen reader label for the button. | +| `...props` | `React.ComponentProps` | - | Spread to the underlying Button component. | + +### `` + +Wrapper for hover preview functionality. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `openDelay` | `number` | `0` | Delay in ms before opening the hover card. | +| `closeDelay` | `number` | `0` | Delay in ms before closing the hover card. | +| `...props` | `React.ComponentProps` | - | Spread to the underlying HoverCard component. | + +### `` + +Trigger element for the hover card. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the underlying HoverCardTrigger component. | + +### `` + +Content displayed in the hover card. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `align` | `unknown` | - | Alignment of the hover card content. | +| `...props` | `React.ComponentProps` | - | Spread to the underlying HoverCardContent component. | + +### `` + +Empty state component when no attachments are present. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the underlying div element. | + +## Utility Functions + +### `getMediaCategory(data)` + +Returns the media category for an attachment. + +```tsx +import { getMediaCategory } from "@/components/ai-elements/attachments"; + +const category = getMediaCategory(attachment); +// Returns: "image" | "video" | "audio" | "document" | "source" | "unknown" +``` + +### `getAttachmentLabel(data)` + +Returns the display label for an attachment. + +```tsx +import { getAttachmentLabel } from "@/components/ai-elements/attachments"; + +const label = getAttachmentLabel(attachment); +// Returns filename or fallback like "Image" or "Attachment" +``` diff --git a/.agents/skills/ai-elements/references/audio-player.md b/.agents/skills/ai-elements/references/audio-player.md new file mode 100644 index 0000000..2bd7b94 --- /dev/null +++ b/.agents/skills/ai-elements/references/audio-player.md @@ -0,0 +1,134 @@ +# Audio Player + +A composable audio player component built on media-chrome, with shadcn styling and flexible controls. + +The `AudioPlayer` component provides a flexible and customizable audio playback interface built on top of media-chrome. It features a composable architecture that allows you to build audio experiences with custom controls, metadata display, and seamless integration with AI-generated audio content. + +See `scripts/audio-player.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add audio-player +``` + +## Features + +- Built on media-chrome for reliable audio playback +- Fully composable architecture with granular control components +- ButtonGroup integration for cohesive control layout +- Individual control components (play, seek, volume, etc.) +- Flexible layout with customizable control bars +- CSS custom properties for deep theming +- Shadcn/ui Button component styling +- Responsive design that works across devices +- Full TypeScript support with proper types for all components + +## Variants + +### AI SDK Speech Result + +The `AudioPlayer` component can be used to play audio from an AI SDK Speech Result. + +See `scripts/audio-player.tsx` for this example. + +### Remote Audio + +The `AudioPlayer` component can be used to play remote audio files. + +See `scripts/audio-player-remote.tsx` for this example. + +## Props + +### `` + +Root MediaController component. Accepts all MediaController props except `audio` (which is set to `true` by default). + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `style` | `CSSProperties` | - | Custom CSS properties can be passed to override media-chrome theming variables. | +| `...props` | `Omit, ` | - | Any other props are spread to the MediaController component. | + +### `` + +The audio element that contains the media source. Accepts either a remote URL or AI SDK Speech Result data. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `src` | `string` | - | The URL of the audio file to play (for remote audio). | +| `data` | `SpeechResult[` | - | AI SDK Speech Result audio data with base64 encoding (for AI-generated audio). | +| `...props` | `Omit` + +Container for control buttons, wraps children in a ButtonGroup. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaControlBar component. | + +### `` + +Play/pause button wrapped in a shadcn Button component. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaPlayButton component. | + +### `` + +Seek backward button wrapped in a shadcn Button component. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `seekOffset` | `number` | `10` | The number of seconds to seek backward. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaSeekBackwardButton component. | + +### `` + +Seek forward button wrapped in a shadcn Button component. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `seekOffset` | `number` | `10` | The number of seconds to seek forward. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaSeekForwardButton component. | + +### `` + +Displays the current playback time, wrapped in ButtonGroupText. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaTimeDisplay component. | + +### `` + +Seek slider for controlling playback position, wrapped in ButtonGroupText. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaTimeRange component. | + +### `` + +Displays the total duration of the audio, wrapped in ButtonGroupText. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaDurationDisplay component. | + +### `` + +Mute/unmute button, wrapped in ButtonGroupText. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaMuteButton component. | + +### `` + +Volume slider control, wrapped in ButtonGroupText. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the MediaVolumeRange component. | diff --git a/.agents/skills/ai-elements/references/canvas.md b/.agents/skills/ai-elements/references/canvas.md new file mode 100644 index 0000000..091d9a3 --- /dev/null +++ b/.agents/skills/ai-elements/references/canvas.md @@ -0,0 +1,34 @@ +# Canvas + +A React Flow-based canvas component for building interactive node-based interfaces. + +The `Canvas` component provides a React Flow-based canvas for building interactive node-based interfaces. It comes pre-configured with sensible defaults for AI applications, including panning, zooming, and selection behaviors. + + + +## Installation + +```bash +npx ai-elements@latest add canvas +``` + +## Features + +- Pre-configured React Flow canvas with AI-optimized defaults +- Pan on scroll enabled for intuitive navigation +- Selection on drag for multi-node operations +- Customizable background color using CSS variables +- Delete key support (Backspace and Delete keys) +- Auto-fit view to show all nodes +- Disabled double-click zoom for better UX +- Disabled pan on drag to prevent accidental canvas movement +- Fully compatible with React Flow props and API + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `ReactNode` | - | Child components like Background, Controls, or MiniMap. | +| `...props` | `ReactFlowProps` | - | Any other React Flow props like nodes, edges, nodeTypes, edgeTypes, onNodesChange, etc. | diff --git a/.agents/skills/ai-elements/references/chain-of-thought.md b/.agents/skills/ai-elements/references/chain-of-thought.md new file mode 100644 index 0000000..8437816 --- /dev/null +++ b/.agents/skills/ai-elements/references/chain-of-thought.md @@ -0,0 +1,81 @@ +# Chain of Thought + +A collapsible component that visualizes AI reasoning steps with support for search results, images, and step-by-step progress indicators. + +The `ChainOfThought` component provides a visual representation of an AI's reasoning process, showing step-by-step thinking with support for search results, images, and progress indicators. It helps users understand how AI arrives at conclusions. + +See `scripts/chain-of-thought.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add chain-of-thought +``` + +## Features + +- Collapsible interface with smooth animations powered by Radix UI +- Step-by-step visualization of AI reasoning process +- Support for different step statuses (complete, active, pending) +- Built-in search results display with badge styling +- Image support with captions for visual content +- Custom icons for different step types +- Context-aware components using React Context API +- Fully typed with TypeScript +- Accessible with keyboard navigation support +- Responsive design that adapts to different screen sizes +- Smooth fade and slide animations for content transitions +- Composable architecture for flexible customization + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `open` | `boolean` | - | Controlled open state of the collapsible. | +| `defaultOpen` | `boolean` | `false` | Default open state when uncontrolled. | +| `onOpenChange` | `(open: boolean) => void` | - | Callback when the open state changes. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the root div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom header text. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `icon` | `LucideIcon` | `DotIcon` | Icon to display for the step. | +| `label` | `string` | - | The main text label for the step. | +| `description` | `string` | - | Optional description text shown below the label. | +| `status` | `unknown` | - | Visual status of the step. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the root div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any props are spread to the container div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any props are spread to the Badge component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any props are spread to the CollapsibleContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `caption` | `string` | - | Optional caption text displayed below the image. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the container div element. | diff --git a/.agents/skills/ai-elements/references/checkpoint.md b/.agents/skills/ai-elements/references/checkpoint.md new file mode 100644 index 0000000..11c110c --- /dev/null +++ b/.agents/skills/ai-elements/references/checkpoint.md @@ -0,0 +1,183 @@ +# Checkpoint + +A simple component for marking conversation history points and restoring the chat to a previous state. + +The `Checkpoint` component provides a way to mark specific points in a conversation history and restore the chat to that state. Inspired by VSCode's Copilot checkpoint feature, it allows users to revert to an earlier conversation state while maintaining a clear visual separation between different conversation segments. + +See `scripts/checkpoint.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add checkpoint +``` + +## Features + +- Simple flex layout with icon, trigger, and separator +- Visual separator line for clear conversation breaks +- Clickable restore button for reverting to checkpoint +- Customizable icon (defaults to BookmarkIcon) +- Keyboard accessible with proper ARIA labels +- Responsive design that adapts to different screen sizes +- Seamless light/dark theme integration + +## Usage with AI SDK + +Build a chat interface with conversation checkpoints that allow users to restore to previous states. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { useState, Fragment } from "react"; +import { useChat } from "@ai-sdk/react"; +import { + Checkpoint, + CheckpointIcon, + CheckpointTrigger, +} from "@/components/ai-elements/checkpoint"; +import { + Message, + MessageContent, + MessageResponse, +} from "@/components/ai-elements/message"; +import { + Conversation, + ConversationContent, +} from "@/components/ai-elements/conversation"; + +type CheckpointType = { + id: string; + messageIndex: number; + timestamp: Date; + messageCount: number; +}; + +const CheckpointDemo = () => { + const { messages, setMessages } = useChat(); + const [checkpoints, setCheckpoints] = useState([]); + + const createCheckpoint = (messageIndex: number) => { + const checkpoint: CheckpointType = { + id: nanoid(), + messageIndex, + timestamp: new Date(), + messageCount: messageIndex + 1, + }; + setCheckpoints([...checkpoints, checkpoint]); + }; + + const restoreToCheckpoint = (messageIndex: number) => { + // Restore messages to checkpoint state + setMessages(messages.slice(0, messageIndex + 1)); + // Remove checkpoints after this point + setCheckpoints(checkpoints.filter((cp) => cp.messageIndex <= messageIndex)); + }; + + return ( +
+ + + {messages.map((message, index) => { + const checkpoint = checkpoints.find( + (cp) => cp.messageIndex === index + ); + + return ( + + + + {message.content} + + + {checkpoint && ( + + + + restoreToCheckpoint(checkpoint.messageIndex) + } + > + Restore checkpoint + + + )} + + ); + })} + + +
+ ); +}; + +export default CheckpointDemo; +``` + +## Use Cases + +### Manual Checkpoints + +Allow users to manually create checkpoints at important conversation points: + +```tsx + +``` + +### Automatic Checkpoints + +Create checkpoints automatically after significant conversation milestones: + +```tsx +useEffect(() => { + // Create checkpoint every 5 messages + if (messages.length > 0 && messages.length % 5 === 0) { + createCheckpoint(messages.length - 1); + } +}, [messages.length]); +``` + +### Branching Conversations + +Use checkpoints to enable conversation branching where users can explore different conversation paths: + +```tsx +const restoreAndBranch = (messageIndex: number) => { + // Save current branch + const currentBranch = messages.slice(messageIndex + 1); + saveBranch(currentBranch); + + // Restore to checkpoint + restoreToCheckpoint(messageIndex); +}; +``` + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | The checkpoint icon and trigger components. Automatically includes a Separator at the end. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom icon content. If not provided, defaults to a BookmarkIcon from lucide-react. | +| `...props` | `LucideProps` | - | Any other props are spread to the BookmarkIcon component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | The text or content to display in the trigger button. | +| `tooltip` | `string` | - | Optional tooltip text shown on hover. | +| `variant` | `string` | - | The button variant style. | +| `size` | `string` | - | The button size. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | diff --git a/.agents/skills/ai-elements/references/code-block.md b/.agents/skills/ai-elements/references/code-block.md new file mode 100644 index 0000000..9a2d65a --- /dev/null +++ b/.agents/skills/ai-elements/references/code-block.md @@ -0,0 +1,170 @@ +# Code Block + +Provides syntax highlighting, line numbers, and copy to clipboard functionality for code blocks. + +The `CodeBlock` component provides syntax highlighting, line numbers, and copy to clipboard functionality for code blocks. It's fully composable, allowing you to customize the header, actions, and content. + +See `scripts/code-block.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add code-block +``` + +## Usage + +The CodeBlock is fully composable. Here's a basic example: + +```tsx +import { + CodeBlock, + CodeBlockActions, + CodeBlockCopyButton, + CodeBlockFilename, + CodeBlockHeader, + CodeBlockTitle, +} from "@/components/ai-elements/code-block"; +import { FileIcon } from "lucide-react"; + +export const Example = () => ( + + + + + example.ts + + + + + + +); +``` + +## Features + +- Syntax highlighting with Shiki +- Line numbers (optional) +- Copy to clipboard functionality +- Automatic light/dark theme switching via CSS variables +- Language selector for multi-language examples +- Fully composable architecture +- Accessible design + +## Examples + +### Dark Mode + +To use the `CodeBlock` component in dark mode, wrap it in a `div` with the `dark` class. + +See `scripts/code-block-dark.tsx` for this example. + +### Language Selector + +Add a language selector to switch between different code implementations: + +See `scripts/code-block.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `code` | `string` | - | The code content to display. | +| `language` | `BundledLanguage` | - | The programming language for syntax highlighting. | +| `showLineNumbers` | `boolean` | `false` | Whether to show line numbers. | +| `children` | `React.ReactNode` | - | Child elements like CodeBlockHeader. | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +Container for the header row. Uses flexbox with `justify-between`. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Header content (CodeBlockTitle, CodeBlockActions, etc.). | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +Left-aligned container for icon and filename. Uses flexbox with `gap-2`. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Title content (icon, CodeBlockFilename, etc.). | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +Displays the filename in monospace font. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | The filename to display. | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +Right-aligned container for action buttons. Uses flexbox with `gap-2`. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Action buttons (CodeBlockCopyButton, CodeBlockLanguageSelector, etc.). | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onCopy` | `() => void` | - | Callback fired after a successful copy. | +| `onError` | `(error: Error) => void` | - | Callback fired if copying fails. | +| `timeout` | `number` | `2000` | How long to show the copied state (ms). | +| `children` | `React.ReactNode` | - | Custom content for the button. Defaults to copy/check icons. | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +Wrapper for the language selector. Extends shadcn/ui Select. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `value` | `string` | - | The currently selected language. | +| `onValueChange` | `(value: string) => void` | - | Callback when the language changes. | +| `children` | `React.ReactNode` | - | Selector components (Trigger, Content, Items). | + +### `` + +Trigger button for the language selector dropdown. Pre-styled for code block header. + +### `` + +Displays the selected language value. + +### `` + +Dropdown content container. Defaults to `align="end"`. + +### `` + +Individual language option in the dropdown. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `value` | `string` | - | The language value. | +| `children` | `React.ReactNode` | - | The display label. | + +### `` + +Low-level container component with performance optimizations (`contentVisibility`). Used internally by CodeBlock. + +### `` + +Low-level component that handles syntax highlighting. Used internally by CodeBlock, but can be used directly for custom layouts. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `code` | `string` | - | The code content to display. | +| `language` | `BundledLanguage` | - | The programming language for syntax highlighting. | +| `showLineNumbers` | `boolean` | `false` | Whether to show line numbers. | diff --git a/.agents/skills/ai-elements/references/commit.md b/.agents/skills/ai-elements/references/commit.md new file mode 100644 index 0000000..08de2c7 --- /dev/null +++ b/.agents/skills/ai-elements/references/commit.md @@ -0,0 +1,177 @@ +# Commit + +Display commit information with hash, message, author, and file changes. + +The `Commit` component displays commit details including hash, message, author, timestamp, and changed files. + +See `scripts/commit.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add commit +``` + +## Features + +- Commit hash display with copy button +- Author avatar with initials +- Relative timestamp formatting +- Collapsible file changes list +- Color-coded file status (added/modified/deleted/renamed) +- Line additions/deletions count + +## File Status + +| Status | Label | Color | +| ---------- | ----- | ------ | +| `added` | A | Green | +| `modified` | M | Yellow | +| `deleted` | D | Red | +| `renamed` | R | Blue | + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the CollapsibleTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `initials` | `string` | Required | Author initials to display. | +| `...props` | `React.ComponentProps` | - | Spread to the Avatar component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom separator content. | +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `date` | `Date` | Required | Commit date. | +| `children` | `React.ReactNode` | - | Custom timestamp content. Defaults to relative time. | +| `...props` | `React.HTMLAttributes` | - | Spread to the time element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `hash` | `string` | Required | Commit hash to copy. | +| `onCopy` | `() => void` | - | Callback after successful copy. | +| `onError` | `(error: Error) => void` | - | Callback if copying fails. | +| `timeout` | `number` | `2000` | Duration to show copied state (ms). | +| `...props` | `React.ComponentProps` | - | Spread to the Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the CollapsibleContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the row div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `status` | `unknown` | Required | File change status. | +| `children` | `React.ReactNode` | - | Custom status label. | +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the FileIcon component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `count` | `number` | Required | Number of lines added. | +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `count` | `number` | Required | Number of lines deleted. | +| `...props` | `React.HTMLAttributes` | - | Spread to the span element. | diff --git a/.agents/skills/ai-elements/references/confirmation.md b/.agents/skills/ai-elements/references/confirmation.md new file mode 100644 index 0000000..eb5b4e3 --- /dev/null +++ b/.agents/skills/ai-elements/references/confirmation.md @@ -0,0 +1,262 @@ +# Confirmation + +An alert-based component for managing tool execution approval workflows with request, accept, and reject states. + +The `Confirmation` component provides a flexible system for displaying tool approval requests and their outcomes. Perfect for showing users when AI tools require approval before execution, and displaying the approval status afterward. + +See `scripts/confirmation.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add confirmation +``` + +## Usage with AI SDK + +Build a chat UI with tool approval workflow where dangerous tools require user confirmation before execution. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { useChat } from "@ai-sdk/react"; +import { DefaultChatTransport, type ToolUIPart } from "ai"; +import { useState } from "react"; +import { CheckIcon, XIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + Confirmation, + ConfirmationTitle, + ConfirmationRequest, + ConfirmationAccepted, + ConfirmationRejected, + ConfirmationActions, + ConfirmationAction, +} from "@/components/ai-elements/confirmation"; +import { MessageResponse } from "@/components/ai-elements/message"; + +type DeleteFileInput = { + filePath: string; + confirm: boolean; +}; + +type DeleteFileToolUIPart = ToolUIPart<{ + delete_file: { + input: DeleteFileInput; + output: { success: boolean; message: string }; + }; +}>; + +const Example = () => { + const { messages, sendMessage, status, addToolApprovalResponse } = useChat({ + transport: new DefaultChatTransport({ + api: "/api/chat", + }), + }); + + const handleDeleteFile = () => { + sendMessage({ text: "Delete the file at /tmp/example.txt" }); + }; + + const latestMessage = messages[messages.length - 1]; + const deleteTool = latestMessage?.parts?.find( + (part) => part.type === "tool-delete_file" + ) as DeleteFileToolUIPart | undefined; + + return ( +
+
+ + + {deleteTool?.approval && ( + + + This tool wants to delete:{" "} + {deleteTool.input?.filePath} +
+ Do you approve this action? +
+ + + You approved this tool execution + + + + You rejected this tool execution + + + + addToolApprovalResponse({ + id: deleteTool.approval!.id, + approved: false, + }) + } + > + Reject + + + addToolApprovalResponse({ + id: deleteTool.approval!.id, + approved: true, + }) + } + > + Approve + + +
+ )} + + {deleteTool?.output && ( + + {deleteTool.output.success + ? deleteTool.output.message + : `Error: ${deleteTool.output.message}`} + + )} +
+
+ ); +}; + +export default Example; +``` + +Add the following route to your backend: + +```ts title="app/api/chat/route.tsx" +import { streamText, UIMessage, convertToModelMessages } from "ai"; +import { z } from "zod"; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { messages }: { messages: UIMessage[] } = await req.json(); + + const result = streamText({ + model: "openai/gpt-4o", + messages: await convertToModelMessages(messages), + tools: { + delete_file: { + description: "Delete a file from the file system", + parameters: z.object({ + filePath: z.string().describe("The path to the file to delete"), + confirm: z + .boolean() + .default(false) + .describe("Confirmation that the user wants to delete the file"), + }), + requireApproval: true, // Enable approval workflow + execute: async ({ filePath, confirm }) => { + if (!confirm) { + return { + success: false, + message: "Deletion not confirmed", + }; + } + + // Simulate file deletion + await new Promise((resolve) => setTimeout(resolve, 500)); + + return { + success: true, + message: `Successfully deleted ${filePath}`, + }; + }, + }, + }, + }); + + return result.toUIMessageStreamResponse(); +} +``` + +## Features + +- Context-based state management for approval workflow +- Conditional rendering based on approval state +- Support for approval-requested, approval-responded, output-denied, and output-available states +- Built on shadcn/ui Alert and Button components +- TypeScript support with comprehensive type definitions +- Customizable styling with Tailwind CSS +- Keyboard navigation and accessibility support +- Theme-aware with automatic dark mode support + +## Examples + +### Approval Request State + +Shows the approval request with action buttons when state is `approval-requested`. + +See `scripts/confirmation-request.tsx` for this example. + +### Approved State + +Shows the accepted status when user approves and state is `approval-responded` or `output-available`. + +See `scripts/confirmation-accepted.tsx` for this example. + +### Rejected State + +Shows the rejected status when user rejects and state is `output-denied`. + +See `scripts/confirmation-rejected.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `approval` | `ToolUIPart[` | - | The approval object containing the approval ID and status. If not provided or undefined, the component will not render. | +| `state` | `ToolUIPart[` | - | The current state of the tool (input-streaming, input-available, approval-requested, approval-responded, output-denied, or output-available). Will not render for input-streaming or input-available states. | +| `className` | `string` | - | Additional CSS classes to apply to the Alert component. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Alert component. | + +### `` + +A styled description element for displaying a title or label within the confirmation alert. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying AlertDescription component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | The content to display when approval is requested. Only renders when state is | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | The content to display when approval is accepted. Only renders when approval.approved is true and state is | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | The content to display when approval is rejected. Only renders when approval.approved is false and state is | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes to apply to the actions container. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the div element. Only renders when state is | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Button component. Styled with h-8 px-3 text-sm classes by default. | diff --git a/.agents/skills/ai-elements/references/connection.md b/.agents/skills/ai-elements/references/connection.md new file mode 100644 index 0000000..606f4f6 --- /dev/null +++ b/.agents/skills/ai-elements/references/connection.md @@ -0,0 +1,34 @@ +# Connection + +A custom connection line component for React Flow-based canvases with animated bezier curve styling. + +The `Connection` component provides a styled connection line for React Flow canvases. It renders an animated bezier curve with a circle indicator at the target end, using consistent theming through CSS variables. + + + +## Installation + +```bash +npx ai-elements@latest add connection +``` + +## Features + +- Smooth bezier curve animation for connection lines +- Visual indicator circle at the target position +- Theme-aware styling using CSS variables +- Cubic bezier curve calculation for natural flow +- Lightweight implementation with minimal props +- Full TypeScript support with React Flow types +- Compatible with React Flow's connection system + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `fromX` | `number` | - | The x-coordinate of the connection start point. | +| `fromY` | `number` | - | The y-coordinate of the connection start point. | +| `toX` | `number` | - | The x-coordinate of the connection end point. | +| `toY` | `number` | - | The y-coordinate of the connection end point. | diff --git a/.agents/skills/ai-elements/references/context.md b/.agents/skills/ai-elements/references/context.md new file mode 100644 index 0000000..772bb23 --- /dev/null +++ b/.agents/skills/ai-elements/references/context.md @@ -0,0 +1,126 @@ +# Context + +A compound component system for displaying AI model context window usage, token consumption, and cost estimation. + +The `Context` component provides a comprehensive view of AI model usage through a compound component system. It displays context window utilization, token consumption breakdown (input, output, reasoning, cache), and cost estimation in an interactive hover card interface. + +See `scripts/context.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add context +``` + +## Features + +- **Compound Component Architecture**: Flexible composition of context display elements +- **Visual Progress Indicator**: Circular SVG progress ring showing context usage percentage +- **Token Breakdown**: Detailed view of input, output, reasoning, and cached tokens +- **Cost Estimation**: Real-time cost calculation using the `tokenlens` library +- **Intelligent Formatting**: Automatic token count formatting (K, M, B suffixes) +- **Interactive Hover Card**: Detailed information revealed on hover +- **Context Provider Pattern**: Clean data flow through React Context API +- **TypeScript Support**: Full type definitions for all components +- **Accessible Design**: Proper ARIA labels and semantic HTML +- **Theme Integration**: Uses currentColor for automatic theme adaptation + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `maxTokens` | `number` | - | The total context window size in tokens. | +| `usedTokens` | `number` | - | The number of tokens currently used. | +| `usage` | `LanguageModelUsage` | - | Detailed token usage breakdown from the AI SDK (input, output, reasoning, cached tokens). | +| `modelId` | `ModelId` | - | Model identifier for cost calculation (e.g., | +| `...props` | `ComponentProps` | - | Any other props are spread to the HoverCard component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom trigger element. If not provided, renders a default button with percentage and icon. | +| `...props` | `ComponentProps + + + + {isLoading && !object && ( +
+ Generating content with citations... +
+ )} + + {object?.content && ( +
+

+ {object.content.split(/(\[\d+\])/).map((part, index) => { + const citationMatch = part.match(/\[(\d+)\]/); + if (citationMatch) { + const citationNumber = citationMatch[1]; + const citation = object.citations?.find( + (c: any) => c.number === citationNumber + ); + + if (citation) { + return ( + + + + + + + + + + + + + + {citation.quote && ( + + {citation.quote} + + )} + + + + + + + ); + } + } + return part; + })} +

+
+ )} + + ); +}; + +export default CitationDemo; +``` + +Add the following route to your backend: + +```ts title="app/api/citation/route.ts" +import { streamObject } from "ai"; +import { z } from "zod"; + +export const citationSchema = z.object({ + content: z.string(), + citations: z.array( + z.object({ + number: z.string(), + title: z.string(), + url: z.string(), + description: z.string().optional(), + quote: z.string().optional(), + }) + ), +}); + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { prompt } = await req.json(); + + const result = streamObject({ + model: "openai/gpt-4o", + schema: citationSchema, + prompt: `Generate a well-researched paragraph about ${prompt} with proper citations. + + Include: + - A comprehensive paragraph with inline citations marked as [1], [2], etc. + - 2-3 citations with realistic source information + - Each citation should have a title, URL, and optional description/quote + - Make the content informative and the sources credible + + Format citations as numbered references within the text.`, + }); + + return result.toTextStreamResponse(); +} +``` + +## Features + +- Hover interaction to reveal detailed citation information +- **Carousel navigation** for multiple citations with prev/next controls +- **Live index tracking** showing current slide position (e.g., "1/5") +- Support for source titles, URLs, and descriptions +- Optional quote blocks for relevant excerpts +- Composable architecture for flexible citation formats +- Accessible design with proper keyboard navigation +- Seamless integration with AI-generated content +- Clean visual design that doesn't disrupt reading flow +- Smart badge display showing source hostname and count + +## Usage with AI SDK + +Currently, there is no official support for inline citations with Streamdown or the Response component. This is because: + +- There isn't any good markdown syntax for inline citations +- Language models don't naturally respond with inline citation syntax +- The AI SDK doesn't have built-in support for inline citations + +### Potential Approaches + +While these methods are hypothetical and not officially supported, there are two conceptual ways inline citations could work with Streamdown: + +1. **Footnote conversion**: GitHub Flavored Markdown (GFM) handles footnotes using `[^1]` syntax. You could hypothetically remove the default footnote rendering and convert footnotes to inline citations instead. + +2. **Custom HTML syntax**: You could add a system prompt instructing the model to use a special HTML syntax like `` and pass that as a custom component to Streamdown. + +These approaches require custom implementation and are not currently supported out of the box. We will investigate official support for this use case in the future. + +For now, the recommended approach is to use `experimental_useObject` (as shown in the usage example above) to generate structured citation data, then manually parse and render inline citations. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the root span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the HoverCard component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `sources` | `string[]` | - | Array of source URLs. The length determines the number displayed in the badge. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying button element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Carousel component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying CarouselContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. Children will override the default index display. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CarouselPrevious component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CarouselNext component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | `string` | - | The title of the source. | +| `url` | `string` | - | The URL of the source. | +| `description` | `string` | - | A brief description of the source. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying blockquote element. | diff --git a/.agents/skills/ai-elements/references/jsx-preview.md b/.agents/skills/ai-elements/references/jsx-preview.md new file mode 100644 index 0000000..ca0eb46 --- /dev/null +++ b/.agents/skills/ai-elements/references/jsx-preview.md @@ -0,0 +1,104 @@ +# JSX Preview + +A component that dynamically renders JSX strings with streaming support for AI-generated UI. + +The `JSXPreview` component renders JSX strings dynamically, supporting streaming scenarios where JSX may be incomplete. It automatically closes unclosed tags during streaming, making it ideal for displaying AI-generated UI components in real-time. + +See `scripts/jsx-preview.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add jsx-preview +``` + +## Features + +- Renders JSX strings dynamically using `react-jsx-parser` +- Streaming support with automatic tag completion +- Custom component injection for rendering your own components +- Error handling with customizable error display +- Context-based architecture for flexible composition + +## Usage with AI SDK + +The JSXPreview component integrates with the AI SDK to render generated UI in real-time: + +```tsx title="components/generated-ui.tsx" +"use client"; + +import { + JSXPreview, + JSXPreviewContent, + JSXPreviewError, +} from "@/components/ai-elements/jsx-preview"; + +type GeneratedUIProps = { + jsx: string; + isStreaming: boolean; +}; + +export const GeneratedUI = ({ jsx, isStreaming }: GeneratedUIProps) => ( + console.error("JSX Parse Error:", error)} + > + + + +); +``` + +### With Custom Components + +You can inject custom components to be used within the rendered JSX: + +```tsx title="components/generated-ui-with-components.tsx" +"use client"; + +import { + JSXPreview, + JSXPreviewContent, +} from "@/components/ai-elements/jsx-preview"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; + +const customComponents = { + Button, + Card, +}; + +export const GeneratedUIWithComponents = ({ jsx }: { jsx: string }) => ( + + + +); +``` + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `jsx` | `string` | Required | The JSX string to render. | +| `isStreaming` | `boolean` | `false` | When true, automatically completes unclosed tags. | +| `components` | `Record` | - | Custom components available within the rendered JSX. | +| `bindings` | `Record` | - | Variables and functions available within the JSX scope. | +| `onError` | `(error: Error) => void` | - | Callback fired when a parsing or rendering error occurs. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `renderError` | `JsxParserProps[` | - | Custom error renderer passed to react-jsx-parser. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `ReactNode | ((error: Error) => ReactNode)` | - | Custom error content or render function receiving the error. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div element. | diff --git a/.agents/skills/ai-elements/references/message.md b/.agents/skills/ai-elements/references/message.md new file mode 100644 index 0000000..bb89ee7 --- /dev/null +++ b/.agents/skills/ai-elements/references/message.md @@ -0,0 +1,237 @@ +# Message + +A comprehensive suite of components for displaying chat messages, including message rendering, branching, actions, and markdown responses. + +The `Message` component suite provides a complete set of tools for building chat interfaces. It includes components for displaying messages from users and AI assistants, managing multiple response branches, adding action buttons, and rendering markdown content. + +See `scripts/message.tsx` for this example. + + + +## Installation + +```bash +npx ai-elements@latest add message +``` + +## Features + +- Displays messages from both user and AI assistant with distinct styling and automatic alignment +- Minimalist flat design with user messages in secondary background and assistant messages full-width +- **Response branching** with navigation controls to switch between multiple AI response versions +- **Markdown rendering** with GFM support (tables, task lists, strikethrough), math equations, and smart streaming +- **Action buttons** for common operations (retry, like, dislike, copy, share) with tooltips and state management +- **File attachments** display with support for images and generic files with preview and remove functionality +- Code blocks with syntax highlighting and copy-to-clipboard functionality +- Keyboard accessible with proper ARIA labels +- Responsive design that adapts to different screen sizes +- Seamless light/dark theme integration + + + +## Usage with AI SDK + +Build a simple chat UI where the user can copy or regenerate the most recent message. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { useState } from "react"; +import { + MessageActions, + MessageAction, +} from "@/components/ai-elements/message"; +import { Message, MessageContent } from "@/components/ai-elements/message"; +import { + Conversation, + ConversationContent, + ConversationScrollButton, +} from "@/components/ai-elements/conversation"; +import { + PromptInput, + type PromptInputMessage, + PromptInputTextarea, + PromptInputSubmit, +} from "@/components/ai-elements/prompt-input"; +import { MessageResponse } from "@/components/ai-elements/message"; +import { RefreshCcwIcon, CopyIcon } from "lucide-react"; +import { useChat } from "@ai-sdk/react"; +import { Fragment } from "react"; + +const ActionsDemo = () => { + const [input, setInput] = useState(""); + const { messages, sendMessage, status, regenerate } = useChat(); + + const handleSubmit = (message: PromptInputMessage) => { + if (message.text.trim()) { + sendMessage({ text: message.text }); + setInput(""); + } + }; + + return ( +
+
+ + + {messages.map((message, messageIndex) => ( + + {message.parts.map((part, i) => { + switch (part.type) { + case "text": + const isLastMessage = + messageIndex === messages.length - 1; + + return ( + + + + {part.text} + + + {message.role === "assistant" && isLastMessage && ( + + regenerate()} + label="Retry" + > + + + + navigator.clipboard.writeText(part.text) + } + label="Copy" + > + + + + )} + + ); + default: + return null; + } + })} + + ))} + + + + + + setInput(e.currentTarget.value)} + className="pr-12" + /> + + +
+
+ ); +}; + +export default ActionsDemo; +``` + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `from` | `UIMessage[` | - | The role of the message sender ( | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the content div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `string` | - | The markdown content to render. | +| `parseIncompleteMarkdown` | `boolean` | `true` | Whether to parse and fix incomplete markdown syntax (e.g., unclosed code blocks or lists). | +| `className` | `string` | - | CSS class names to apply to the wrapper div element. | +| `components` | `object` | - | Custom React components to use for rendering markdown elements (e.g., custom heading, paragraph, code block components). | +| `allowedImagePrefixes` | `string[]` | `[` | Array of allowed URL prefixes for images. Use [ | +| `allowedLinkPrefixes` | `string[]` | `[` | Array of allowed URL prefixes for links. Use [ | +| `defaultOrigin` | `string` | - | Default origin to use for relative URLs in links and images. | +| `rehypePlugins` | `array` | `[rehypeKatex]` | Array of rehype plugins to use for processing HTML. Includes KaTeX for math rendering by default. | +| `remarkPlugins` | `array` | `[remarkGfm, remarkMath]` | Array of remark plugins to use for processing markdown. Includes GitHub Flavored Markdown and math support by default. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | HTML attributes to spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `tooltip` | `string` | - | Optional tooltip text shown on hover. | +| `label` | `string` | - | Accessible label for screen readers. Also used as fallback if tooltip is not provided. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `defaultBranch` | `number` | `0` | The index of the branch to show by default. | +| `onBranchChange` | `(branchIndex: number) => void` | - | Callback fired when the branch changes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying ButtonGroup component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the underlying span element. | + +### `` + +A container for placing actions and branch selectors below a message. Lays out children in a horizontal row with space-between alignment. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the root div. | +``` diff --git a/.agents/skills/ai-elements/references/mic-selector.md b/.agents/skills/ai-elements/references/mic-selector.md new file mode 100644 index 0000000..8f01915 --- /dev/null +++ b/.agents/skills/ai-elements/references/mic-selector.md @@ -0,0 +1,189 @@ +# Mic Selector + +A composable dropdown component for selecting audio input devices with permission handling and device change detection. + +The `MicSelector` component provides a flexible and composable interface for selecting microphone input devices. Built on shadcn/ui's Command and Popover components, it features automatic device detection, permission handling, dynamic device list updates, and intelligent device name parsing. + +See `scripts/mic-selector.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add mic-selector +``` + +## Features + +- Fully composable architecture with granular control components +- Automatic audio input device enumeration +- Permission-based device name display +- Real-time device change detection via devicechange events +- Intelligent device label parsing with ID extraction +- Controlled and uncontrolled component patterns +- Responsive width matching between trigger and content +- Built on shadcn/ui Command and Popover components +- Full TypeScript support with proper types for all components + +## Props + +### `` + +Root Popover component that provides context for all child components. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `defaultValue` | `string` | - | The default selected device ID (uncontrolled). | +| `value` | `string` | - | The selected device ID (controlled). | +| `onValueChange` | `(deviceId: string) => void` | - | Callback fired when the selected device changes. | +| `defaultOpen` | `boolean` | `false` | The default open state (uncontrolled). | +| `open` | `boolean` | - | The open state (controlled). | +| `onOpenChange` | `(open: boolean) => void` | - | Callback fired when the open state changes. Automatically requests microphone permission when opened without permission. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Popover component. | + +### `` + +Button that opens the microphone selector popover. Automatically tracks its width to match the popover content. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Button component. | + +### `` + +Displays the currently selected microphone name or a placeholder. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the span element. | + +### `` + +Container for the Command component, rendered inside the popover. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `popoverOptions` | `React.ComponentProps` | - | Props to pass to the underlying PopoverContent component. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Command component. | + +### `` + +Search input for filtering microphones. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandInput component. | + +### `` + +Wrapper for the list of microphone items. Uses render props pattern to provide access to device data. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `(devices: MediaDeviceInfo[]) => ReactNode` | - | Render function that receives the array of available devices. | +| `...props` | `Omit, ` | - | Any other props are spread to the CommandList component. | + +### `` + +Message shown when no microphones match the search. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `ReactNode` | - | The message to display. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandEmpty component. | + +### `` + +Selectable item representing a microphone. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `value` | `string` | - | The device ID for this item. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandItem component. | + +### `` + +Displays a formatted microphone label with intelligent device ID parsing. Automatically extracts and styles device IDs in the format (XXXX:XXXX). + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `device` | `MediaDeviceInfo` | - | The MediaDeviceInfo object for the device. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the span element. | + +## Hooks + +### `useAudioDevices()` + +A custom hook for managing audio input devices. This hook is used internally by the `MicSelector` component but can also be used independently. + +```tsx +import { useAudioDevices } from "@repo/elements/mic-selector"; + +export default function Example() { + const { devices, loading, error, hasPermission, loadDevices } = + useAudioDevices(); + + return ( +
+ {loading &&

Loading devices...

} + {error &&

Error: {error}

} + {devices.map((device) => ( +
{device.label}
+ ))} + {!hasPermission && ( + + )} +
+ ); +} +``` + +#### Return Value + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `devices` | `MediaDeviceInfo[]` | - | Array of available audio input devices. | +| `loading` | `boolean` | - | Whether devices are currently being loaded. | +| `error` | `string | null` | - | Error message if device loading failed. | +| `hasPermission` | `boolean` | - | Whether microphone permission has been granted. | +| `loadDevices` | `() => Promise` | - | Function to request microphone permission and load device names. | + +## Behavior + +### Permission Handling + +The component implements a two-stage permission approach: + +1. **Without Permission**: Initially loads devices without requesting permission. Device labels may show as generic names (e.g., "Microphone 1"). +2. **With Permission**: When the popover is opened and permission hasn't been granted, automatically requests microphone access and displays actual device names. + +### Device Label Parsing + +The `MicSelectorLabel` component intelligently parses device names that include hardware IDs in the format `(XXXX:XXXX)`. It splits the label into the device name and ID, styling the ID with muted text for better readability. + +For example: `"MacBook Pro Microphone (1a2b:3c4d)"` becomes: + +- Device name: `"MacBook Pro Microphone"` +- Device ID: `"(1a2b:3c4d)"` (styled with muted color) + +### Width Synchronization + +The `MicSelectorTrigger` uses a ResizeObserver to track its width and automatically synchronizes it with the `MicSelectorContent` popover width for a cohesive appearance. + +### Device Change Detection + +The component listens for `devicechange` events (e.g., plugging/unplugging microphones) and automatically updates the device list in real-time. + +## Accessibility + +- Uses semantic HTML with proper ARIA attributes via shadcn/ui components +- Full keyboard navigation support through Command component +- Screen reader friendly with proper labels and roles +- Searchable device list for quick selection + +## Notes + +- Requires a secure context (HTTPS or localhost) for microphone access +- Browser may prompt user for microphone permission on first open +- Device labels are only fully descriptive after permission is granted +- Component handles cleanup of temporary media streams during permission requests +- Uses Radix UI's `useControllableState` for flexible controlled/uncontrolled patterns diff --git a/.agents/skills/ai-elements/references/model-selector.md b/.agents/skills/ai-elements/references/model-selector.md new file mode 100644 index 0000000..86b1834 --- /dev/null +++ b/.agents/skills/ai-elements/references/model-selector.md @@ -0,0 +1,112 @@ +# Model Selector + +A searchable command palette for selecting AI models in your chat interface. + +The `ModelSelector` component provides a searchable command palette interface for selecting AI models. It's built on top of the cmdk library and provides a keyboard-navigable interface with search functionality. + +See `scripts/model-selector.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add model-selector +``` + +## Features + +- Searchable interface with keyboard navigation +- Fuzzy search filtering across model names +- Grouped model organization by provider +- Keyboard shortcuts support +- Empty state handling +- Customizable styling with Tailwind CSS +- Built on cmdk for excellent accessibility +- TypeScript support with proper type definitions + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Dialog component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DialogTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | `ReactNode` | - | Accessible title for the dialog (rendered in sr-only). | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DialogContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandDialog component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandInput component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandList component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandEmpty component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandGroup component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandShortcut component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CommandSeparator component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `provider` | `string` | Required | The AI provider name. Supports major providers like | +| `...props` | `Omit` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying span element. | diff --git a/.agents/skills/ai-elements/references/node.md b/.agents/skills/ai-elements/references/node.md new file mode 100644 index 0000000..ff1da06 --- /dev/null +++ b/.agents/skills/ai-elements/references/node.md @@ -0,0 +1,73 @@ +# Node + +A composable node component for React Flow-based canvases with Card-based styling. + +The `Node` component provides a composable, Card-based node for React Flow canvases. It includes support for connection handles, structured layouts, and consistent styling using shadcn/ui components. + + + +## Installation + +```bash +npx ai-elements@latest add node +``` + +## Features + +- Built on shadcn/ui Card components for consistent styling +- Automatic handle placement (left for target, right for source) +- Composable sub-components (Header, Title, Description, Action, Content, Footer) +- Semantic structure for organizing node information +- Pre-styled sections with borders and backgrounds +- Responsive sizing with fixed small width +- Full TypeScript support with proper type definitions +- Compatible with React Flow's node system + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `handles` | `unknown` | - | Configuration for connection handles. Target renders on the left, source on the right. | +| `className` | `string` | - | Additional CSS classes to apply to the node. | +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying Card component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes to apply to the header. | +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying CardHeader component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying CardTitle component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying CardDescription component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying CardAction component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes to apply to the content. | +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying CardContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes to apply to the footer. | +| `...props` | `ComponentProps` | - | Any other props are spread to the underlying CardFooter component. | diff --git a/.agents/skills/ai-elements/references/open-in-chat.md b/.agents/skills/ai-elements/references/open-in-chat.md new file mode 100644 index 0000000..29327c4 --- /dev/null +++ b/.agents/skills/ai-elements/references/open-in-chat.md @@ -0,0 +1,67 @@ +# Open In Chat + +A dropdown menu for opening queries in various AI chat platforms including ChatGPT, Claude, T3, Scira, and v0. + +The `OpenIn` component provides a dropdown menu that allows users to open queries in different AI chat platforms with a single click. + +See `scripts/open-in-chat.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add open-in-chat +``` + +## Features + +- Pre-configured links to popular AI chat platforms +- Context-based query passing for cleaner API +- Customizable dropdown trigger button +- Automatic URL parameter encoding for queries +- Support for ChatGPT, Claude, T3 Chat, Scira AI, v0, and Cursor +- Branded icons for each platform +- TypeScript support with proper type definitions +- Accessible dropdown menu with keyboard navigation +- External link indicators for clarity + +## Supported Platforms + +- **ChatGPT** - Opens query in OpenAI's ChatGPT with search hints +- **Claude** - Opens query in Anthropic's Claude AI +- **T3 Chat** - Opens query in T3 Chat platform +- **Scira AI** - Opens query in Scira's AI assistant +- **v0** - Opens query in Vercel's v0 platform +- **Cursor** - Opens query in Cursor AI editor + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `query` | `string` | - | The query text to be sent to all AI platforms. | +| `...props` | `React.ComponentProps` | - | Props to spread to the underlying radix-ui DropdownMenu component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom trigger button. | +| `...props` | `React.ComponentProps` | - | Props to spread to the underlying DropdownMenuTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes to apply to the dropdown content. | +| `...props` | `React.ComponentProps` | - | Props to spread to the underlying DropdownMenuContent component. | + +### ``, ``, ``, ``, ``, `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Props to spread to the underlying DropdownMenuItem component. The query is automatically provided via context from the parent OpenIn component. | + +### ``, ``, `` + +Additional composable components for custom dropdown menu items, labels, and separators that follow the same props pattern as their underlying radix-ui counterparts. diff --git a/.agents/skills/ai-elements/references/package-info.md b/.agents/skills/ai-elements/references/package-info.md new file mode 100644 index 0000000..52eb165 --- /dev/null +++ b/.agents/skills/ai-elements/references/package-info.md @@ -0,0 +1,95 @@ +# Package Info + +Display dependency information and version changes. + +The `PackageInfo` component displays package dependency information including version changes and change type badges. + +See `scripts/package-info.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add package-info +``` + +## Features + +- Version change display (current → new) +- Color-coded change type badges +- Dependencies list +- Description support + +## Change Types + +| Type | Color | Use Case | +| --------- | ------ | ------------------ | +| `major` | Red | Breaking changes | +| `minor` | Yellow | New features | +| `patch` | Green | Bug fixes | +| `added` | Blue | New dependency | +| `removed` | Gray | Removed dependency | + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `name` | `string` | Required | Package name. | +| `currentVersion` | `string` | - | Current installed version. | +| `newVersion` | `string` | - | New version being installed. | +| `changeType` | `unknown` | - | Type of version change. | +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the header div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom name content. Defaults to the name from context. | +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom change type label. Defaults to the changeType from context. | +| `...props` | `React.HTMLAttributes` | - | Spread to the Badge component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom version content. Defaults to version transition display. | +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the p element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `name` | `string` | Required | Dependency name. | +| `version` | `string` | - | Dependency version. | +| `...props` | `React.HTMLAttributes` | - | Spread to the row div. | diff --git a/.agents/skills/ai-elements/references/panel.md b/.agents/skills/ai-elements/references/panel.md new file mode 100644 index 0000000..bde5528 --- /dev/null +++ b/.agents/skills/ai-elements/references/panel.md @@ -0,0 +1,33 @@ +# Panel + +A styled panel component for React Flow-based canvases to position custom UI elements. + +The `Panel` component provides a positioned container for custom UI elements on React Flow canvases. It includes modern card styling with backdrop blur and flexible positioning options. + + + +## Installation + +```bash +npx ai-elements@latest add panel +``` + +## Features + +- Flexible positioning (top-left, top-right, bottom-left, bottom-right, top-center, bottom-center) +- Rounded pill design with backdrop blur +- Theme-aware card background +- Flexbox layout for easy content alignment +- Subtle drop shadow for depth +- Full TypeScript support +- Compatible with React Flow's panel system + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `position` | `unknown` | - | Position of the panel on the canvas. | +| `className` | `string` | - | Additional CSS classes to apply to the panel. | +| `...props` | `ComponentProps` | - | Any other props from @xyflow/react Panel component. | diff --git a/.agents/skills/ai-elements/references/persona.md b/.agents/skills/ai-elements/references/persona.md new file mode 100644 index 0000000..3ef10dd --- /dev/null +++ b/.agents/skills/ai-elements/references/persona.md @@ -0,0 +1,166 @@ +# Persona + +An animated AI visual component powered by Rive that responds to different states like listening, thinking, and speaking. + +The `Persona` component displays an animated AI visual that responds to different conversational states. Built with Rive WebGL2, it provides smooth, high-performance animations for various AI interaction states including idle, listening, thinking, speaking, and asleep. The component supports multiple visual variants to match different design aesthetics. + +See `scripts/persona-obsidian.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add persona +``` + +## Features + +- Smooth state-based animations powered by Rive +- Multiple visual variants (obsidian, mana, opal, halo, glint, command) +- Responsive to five distinct states: idle, listening, thinking, speaking, and asleep +- WebGL2-accelerated rendering for optimal performance +- Customizable size and styling +- Lifecycle callbacks for load, ready, pause, play, and stop events +- TypeScript support with full type definitions + +## Variants + +The Persona component comes with 6 distinct visual variants, each with its own unique aesthetic: + +### Obsidian (Default) + +See `scripts/persona-obsidian.tsx` for this example. + +### Mana + +See `scripts/persona-mana.tsx` for this example. + +### Opal + +See `scripts/persona-opal.tsx` for this example. + +### Halo + +See `scripts/persona-halo.tsx` for this example. + +### Glint + +See `scripts/persona-glint.tsx` for this example. + +### Command + +See `scripts/persona-command.tsx` for this example. + +## Props + +### `` + +The root component that renders the animated AI visual. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `state` | `unknown` | - | The current state of the AI persona. Controls which animation is displayed. | +| `variant` | `unknown` | - | The visual style variant to display. | +| `className` | `string` | - | Additional CSS classes to apply to the component. | +| `onLoad` | `RiveParameters[` | - | Callback fired when the Rive file starts loading. | +| `onLoadError` | `RiveParameters[` | - | Callback fired if the Rive file fails to load. | +| `onReady` | `() => void` | - | Callback fired when the Rive animation is ready to play. | +| `onPause` | `RiveParameters[` | - | Callback fired when the animation is paused. | +| `onPlay` | `RiveParameters[` | - | Callback fired when the animation starts playing. | +| `onStop` | `RiveParameters[` | - | Callback fired when the animation is stopped. | + +## States + +The Persona component responds to five distinct states, each triggering different animations: + +- **idle**: The default resting state when the AI is not active +- **listening**: Displayed when the AI is actively listening to user input (e.g., during voice recording) +- **thinking**: Shown when the AI is processing or generating a response +- **speaking**: Active when the AI is delivering a response (e.g., text-to-speech output) +- **asleep**: A dormant state for when the AI is inactive or in low-power mode + +## React Strict Mode (Vite) + +The Persona component uses WebGL2 for rendering. Browsers limit the number of active WebGL2 contexts (~8–16), and React Strict Mode (enabled by default in Vite dev) double-mounts components, which can exhaust that limit and crash the page. + +The component includes a built-in guard that defers WebGL2 initialization by one frame, preventing context creation during Strict Mode's throw-away mount. This means the component works in Vite dev mode out of the box — no configuration needed. + +If you still experience crashes (for example, when rendering many Persona instances simultaneously), reduce the number of concurrent Persona components on screen. + +## Usage Examples + +### Basic Usage + +```tsx +import { Persona } from "@repo/elements/persona"; + +export default function App() { + return ; +} +``` + +### With State Management + +```tsx +import { Persona } from "@repo/elements/persona"; +import { useState } from "react"; + +export default function App() { + const [state, setState] = useState< + "idle" | "listening" | "thinking" | "speaking" | "asleep" + >("idle"); + + const startListening = () => setState("listening"); + const startThinking = () => setState("thinking"); + const startSpeaking = () => setState("speaking"); + const reset = () => setState("idle"); + + return ( +
+ +
+ + + + +
+
+ ); +} +``` + +### With Custom Styling + +```tsx +import { Persona } from "@repo/elements/persona"; + +export default function App() { + return ( + + ); +} +``` + +### With Lifecycle Callbacks + +```tsx +import { Persona } from "@repo/elements/persona"; + +export default function App() { + return ( + console.log("Animation ready")} + onLoad={() => console.log("Starting to load")} + onLoadError={(error) => console.error("Failed to load:", error)} + onPlay={() => console.log("Animation playing")} + onPause={() => console.log("Animation paused")} + onStop={() => console.log("Animation stopped")} + /> + ); +} +``` diff --git a/.agents/skills/ai-elements/references/plan.md b/.agents/skills/ai-elements/references/plan.md new file mode 100644 index 0000000..58a41a0 --- /dev/null +++ b/.agents/skills/ai-elements/references/plan.md @@ -0,0 +1,79 @@ +# Plan + +A collapsible plan component for displaying AI-generated execution plans with streaming support and shimmer animations. + +The `Plan` component provides a flexible system for displaying AI-generated execution plans with collapsible content. Perfect for showing multi-step workflows, task breakdowns, and implementation strategies with support for streaming content and loading states. + +See `scripts/plan.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add plan +``` + +## Features + +- Collapsible content with smooth animations +- Streaming support with shimmer loading states +- Built on shadcn/ui Card and Collapsible components +- TypeScript support with comprehensive type definitions +- Customizable styling with Tailwind CSS +- Responsive design with mobile-friendly interactions +- Keyboard navigation and accessibility support +- Theme-aware with automatic dark mode support +- Context-based state management for streaming + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `isStreaming` | `boolean` | `false` | Whether content is currently streaming. Enables shimmer animations on title and description. | +| `defaultOpen` | `boolean` | - | Whether the plan is expanded by default. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CardHeader component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `string` | - | The title text. Displays with shimmer animation when isStreaming is true. | +| `...props` | `Omit, ` | - | Any other props (except children) are spread to the CardTitle component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `string` | - | The description text. Displays with shimmer animation when isStreaming is true. | +| `...props` | `Omit, ` | - | Any other props (except children) are spread to the CardDescription component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger component. Renders as a Button with chevron icon. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CardContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CardAction component. | diff --git a/.agents/skills/ai-elements/references/prompt-input.md b/.agents/skills/ai-elements/references/prompt-input.md new file mode 100644 index 0000000..681eeff --- /dev/null +++ b/.agents/skills/ai-elements/references/prompt-input.md @@ -0,0 +1,579 @@ +# Prompt Input + +Allows a user to send a message with file attachments to a large language model. It includes a textarea, file upload capabilities, a submit button, and a dropdown for selecting the model. + +The `PromptInput` component allows a user to send a message with file attachments to a large language model. It includes a textarea, file upload capabilities, a submit button, and a dropdown for selecting the model. + +See `scripts/prompt-input.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add prompt-input +``` + +## Usage with AI SDK + +Build a fully functional chat app using `PromptInput`, [`Conversation`](/components/conversation) with a model picker: + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { + Attachment, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { + PromptInput, + PromptInputActionAddAttachments, + PromptInputActionAddScreenshot, + PromptInputActionMenu, + PromptInputActionMenuContent, + PromptInputActionMenuTrigger, + PromptInputBody, + PromptInputButton, + PromptInputHeader, + type PromptInputMessage, + PromptInputSelect, + PromptInputSelectContent, + PromptInputSelectItem, + PromptInputSelectTrigger, + PromptInputSelectValue, + PromptInputSubmit, + PromptInputTextarea, + PromptInputFooter, + PromptInputTools, + usePromptInputAttachments, +} from "@/components/ai-elements/prompt-input"; +import { GlobeIcon } from "lucide-react"; +import { useState } from "react"; +import { useChat } from "@ai-sdk/react"; +import { + Conversation, + ConversationContent, + ConversationScrollButton, +} from "@/components/ai-elements/conversation"; +import { + Message, + MessageContent, + MessageResponse, +} from "@/components/ai-elements/message"; + +const PromptInputAttachmentsDisplay = () => { + const attachments = usePromptInputAttachments(); + + if (attachments.files.length === 0) { + return null; + } + + return ( + + {attachments.files.map((attachment) => ( + attachments.remove(attachment.id)} + > + + + + ))} + + ); +}; + +const models = [ + { id: "gpt-4o", name: "GPT-4o" }, + { id: "claude-opus-4-20250514", name: "Claude 4 Opus" }, +]; + +const InputDemo = () => { + const [text, setText] = useState(""); + const [model, setModel] = useState(models[0].id); + const [useWebSearch, setUseWebSearch] = useState(false); + + const { messages, status, sendMessage } = useChat(); + + const handleSubmit = (message: PromptInputMessage) => { + const hasText = Boolean(message.text); + const hasAttachments = Boolean(message.files?.length); + + if (!(hasText || hasAttachments)) { + return; + } + + sendMessage( + { + text: message.text || "Sent with attachments", + files: message.files, + }, + { + body: { + model: model, + webSearch: useWebSearch, + }, + } + ); + setText(""); + }; + + return ( +
+
+ + + {messages.map((message) => ( + + + {message.parts.map((part, i) => { + switch (part.type) { + case "text": + return ( + + {part.text} + + ); + default: + return null; + } + })} + + + ))} + + + + + + + + + + setText(e.target.value)} + value={text} + /> + + + + + + + + + + + setUseWebSearch(!useWebSearch)} + tooltip={{ content: "Search the web", shortcut: "⌘K" }} + variant={useWebSearch ? "default" : "ghost"} + > + + Search + + { + setModel(value); + }} + value={model} + > + + + + + {models.map((model) => ( + + {model.name} + + ))} + + + + + + +
+
+ ); +}; + +export default InputDemo; +``` + +Add the following route to your backend: + +```ts title="app/api/chat/route.ts" +import { streamText, UIMessage, convertToModelMessages } from "ai"; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { + model, + messages, + webSearch, + }: { + messages: UIMessage[]; + model: string; + webSearch?: boolean; + } = await req.json(); + + const result = streamText({ + model: webSearch ? "perplexity/sonar" : model, + messages: await convertToModelMessages(messages), + }); + + return result.toUIMessageStreamResponse(); +} +``` + +## Features + +- Auto-resizing textarea that adjusts height based on content +- File attachment support with drag-and-drop +- Built-in screenshot capture action +- Image preview for image attachments +- Configurable file constraints (max files, max size, accepted types) +- Automatic submit button icons based on status +- Support for keyboard shortcuts (Enter to submit, Shift+Enter for new line) +- Customizable min/max height for the textarea +- Flexible toolbar with support for custom actions and tools +- Built-in model selection dropdown +- Built-in native speech recognition button (Web Speech API) +- Optional provider for lifted state management +- Form automatically resets on submit +- Responsive design with mobile-friendly controls +- Clean, modern styling with customizable themes +- Form-based submission handling +- Hidden file input sync for native form posts +- Global document drop support (opt-in) + +## Examples + +### Cursor style + +See `scripts/prompt-input-cursor.tsx` for this example. + +### Button tooltips + +Buttons can display tooltips with optional keyboard shortcut hints. Hover over the buttons below to see the tooltips. + +See `scripts/prompt-input-tooltip.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onSubmit` | `(message: PromptInputMessage, event: FormEvent) => void` | - | Handler called when the form is submitted with message text and files. | +| `accept` | `string` | - | File types to accept (e.g., | +| `multiple` | `boolean` | - | Whether to allow multiple file selection. | +| `globalDrop` | `boolean` | - | When true, accepts file drops anywhere on the document. | +| `syncHiddenInput` | `boolean` | - | Render a hidden input with given name for native form posts. | +| `maxFiles` | `number` | - | Maximum number of files allowed. | +| `maxFileSize` | `number` | - | Maximum file size in bytes. | +| `onError` | `(err: { code: ` | - | Handler for file validation errors. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root form element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Textarea component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the toolbar div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the tools div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `tooltip` | `string | { content: ReactNode; shortcut?: string; side?: ` | - | Optional tooltip to display on hover. Can be a string or an object with content, shortcut, and side properties. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +#### Tooltip Examples + +```tsx +// Simple string tooltip + + + + +// Tooltip with keyboard shortcut hint + + + + +// Tooltip with custom position + + + +``` + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `status` | `ChatStatus` | - | Current chat status to determine button icon (submitted, streaming, error). | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Select component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying SelectTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying SelectContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying SelectItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying SelectValue component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the body div. | + +### Attachments + +Attachment components have been moved to a separate module. See the [Attachment](/components/attachment) component documentation for details on ``, ``, ``, ``, and ``. + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DropdownMenu component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DropdownMenuContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DropdownMenuItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `label` | `string` | - | Label for the menu item. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DropdownMenuItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `label` | `string` | - | Label for the menu item. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying DropdownMenuItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `initialInput` | `string` | - | Initial text input value. | +| `children` | `React.ReactNode` | - | Child components that will have access to the provider context. | + +Optional global provider that lifts PromptInput state outside of PromptInput. When used, it allows you to access and control the input state from anywhere within the provider tree. If not used, PromptInput stays fully self-managed. + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `Omit, ` | - | Any other props (except align) are spread to the InputGroupAddon component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `openDelay` | `number` | `0` | Delay in milliseconds before opening. | +| `closeDelay` | `number` | `0` | Delay in milliseconds before closing. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the HoverCard component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the HoverCardTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `align` | `unknown` | - | Alignment of the hover card content. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the HoverCardContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the h3 element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Command component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandInput component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandList component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandEmpty component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandGroup component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandItem component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CommandSeparator component. | + +## Hooks + +### `usePromptInputAttachments` + +Access and manage file attachments within a PromptInput context. + +```tsx +const attachments = usePromptInputAttachments(); + +// Available methods: +attachments.files; // Array of current attachments +attachments.add(files); // Add new files +attachments.remove(id); // Remove an attachment by ID +attachments.clear(); // Clear all attachments +attachments.openFileDialog(); // Open file selection dialog +``` + +### `usePromptInputController` + +Access the full PromptInput controller from a PromptInputProvider. Only available when using the provider. + +```tsx +const controller = usePromptInputController(); + +// Available methods: +controller.textInput.value; // Current text input value +controller.textInput.setInput(value); // Set text input value +controller.textInput.clear(); // Clear text input +controller.attachments; // Same as usePromptInputAttachments +``` + +### `useProviderAttachments` + +Access attachments context from a PromptInputProvider. Only available when using the provider. + +```tsx +const attachments = useProviderAttachments(); + +// Same interface as usePromptInputAttachments +``` + +### `usePromptInputReferencedSources` + +Access referenced sources context within a PromptInput. + +```tsx +const sources = usePromptInputReferencedSources(); + +// Available methods: +sources.sources; // Array of current referenced sources +sources.add(sources); // Add new source(s) +sources.remove(id); // Remove a source by ID +sources.clear(); // Clear all sources +``` diff --git a/.agents/skills/ai-elements/references/queue.md b/.agents/skills/ai-elements/references/queue.md new file mode 100644 index 0000000..6d265b4 --- /dev/null +++ b/.agents/skills/ai-elements/references/queue.md @@ -0,0 +1,172 @@ +# Queue + +A comprehensive queue component system for displaying message lists, todos, and collapsible task sections in AI applications. + +The `Queue` component provides a flexible system for displaying lists of messages, todos, attachments, and collapsible sections. Perfect for showing AI workflow progress, pending tasks, message history, or any structured list of items in your application. + +See `scripts/queue.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add queue +``` + +## Features + +- Flexible component system with composable parts +- Collapsible sections with smooth animations +- Support for completed/pending state indicators +- Built-in scroll area for long lists +- Attachment display with images and file indicators +- Hover-revealed action buttons for queue items +- TypeScript support with comprehensive type definitions +- Customizable styling with Tailwind CSS +- Responsive design with mobile-friendly interactions +- Keyboard navigation and accessibility support +- Theme-aware with automatic dark mode support + +## Examples + +### With PromptInput + +See `scripts/queue-prompt-input.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `defaultOpen` | `boolean` | `true` | Whether the section is open by default. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the button element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `label` | `string` | - | The label text to display. | +| `count` | `number` | - | The count to display before the label. | +| `icon` | `React.ReactNode` | - | An optional icon to display before the count. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the ScrollArea component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the li element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `completed` | `boolean` | `false` | Whether the item is completed. Affects the indicator styling. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `completed` | `boolean` | `false` | Whether the item is completed. Affects text styling with strikethrough and opacity. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `completed` | `boolean` | `false` | Whether the item is completed. Affects text styling. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `Omit, ` | - | Any other props (except variant and size) are spread to the Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the img element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the span element. | + +## Type Exports + +### `QueueMessagePart` + +Interface for message parts within queue messages. + +```tsx +interface QueueMessagePart { + type: string; + text?: string; + url?: string; + filename?: string; + mediaType?: string; +} +``` + +### `QueueMessage` + +Interface for queue message items. + +```tsx +interface QueueMessage { + id: string; + parts: QueueMessagePart[]; +} +``` + +### `QueueTodo` + +Interface for todo items in the queue. + +```tsx +interface QueueTodo { + id: string; + title: string; + description?: string; + status?: "pending" | "completed"; +} +``` diff --git a/.agents/skills/ai-elements/references/reasoning.md b/.agents/skills/ai-elements/references/reasoning.md new file mode 100644 index 0000000..9563bc6 --- /dev/null +++ b/.agents/skills/ai-elements/references/reasoning.md @@ -0,0 +1,238 @@ +# Reasoning + +A collapsible component that displays AI reasoning content, automatically opening during streaming and closing when finished. + +The `Reasoning` component displays AI reasoning content, automatically opening during streaming and closing when finished. + +See `scripts/reasoning.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add reasoning +``` + +## Usage with AI SDK + +Build a chatbot with reasoning using Deepseek R1 or other reasoning models. + +Some models (like GPT with high reasoning effort) return multiple reasoning parts instead of a single streaming block. The example below consolidates all reasoning parts into a single component to avoid displaying multiple "Thinking..." indicators. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { + Reasoning, + ReasoningContent, + ReasoningTrigger, +} from "@/components/ai-elements/reasoning"; +import { + Conversation, + ConversationContent, + ConversationScrollButton, +} from "@/components/ai-elements/conversation"; +import { + PromptInput, + type PromptInputMessage, + PromptInputTextarea, + PromptInputSubmit, +} from "@/components/ai-elements/prompt-input"; +import { Spinner } from "@/components/ui/spinner"; +import { + Message, + MessageContent, + MessageResponse, +} from "@/components/ai-elements/message"; +import { useState } from "react"; +import { useChat } from "@ai-sdk/react"; +import type { UIMessage } from "ai"; + +const MessageParts = ({ + message, + isLastMessage, + isStreaming, +}: { + message: UIMessage; + isLastMessage: boolean; + isStreaming: boolean; +}) => { + // Consolidate all reasoning parts into one block + const reasoningParts = message.parts.filter( + (part) => part.type === "reasoning" + ); + const reasoningText = reasoningParts.map((part) => part.text).join("\n\n"); + const hasReasoning = reasoningParts.length > 0; + + // Check if reasoning is still streaming (last part is reasoning on last message) + const lastPart = message.parts.at(-1); + const isReasoningStreaming = + isLastMessage && isStreaming && lastPart?.type === "reasoning"; + + return ( + <> + {hasReasoning && ( + + + {reasoningText} + + )} + {message.parts.map((part, i) => { + if (part.type === "text") { + return ( + + {part.text} + + ); + } + return null; + })} + + ); +}; + +const ReasoningDemo = () => { + const [input, setInput] = useState(""); + + const { messages, sendMessage, status } = useChat(); + + const handleSubmit = (message: PromptInputMessage) => { + sendMessage({ text: message.text }); + setInput(""); + }; + + const isStreaming = status === "streaming"; + + return ( +
+
+ + + {messages.map((message, index) => ( + + + + + + ))} + {status === "submitted" && } + + + + + + setInput(e.currentTarget.value)} + className="pr-12" + /> + + +
+
+ ); +}; + +export default ReasoningDemo; +``` + +Add the following route to your backend: + +```ts title="app/api/chat/route.ts" +import { streamText, UIMessage, convertToModelMessages } from "ai"; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { model, messages }: { messages: UIMessage[]; model: string } = + await req.json(); + + const result = streamText({ + model: "deepseek/deepseek-r1", + messages: await convertToModelMessages(messages), + }); + + return result.toUIMessageStreamResponse({ + sendReasoning: true, + }); +} +``` + +## Reasoning vs Chain of Thought + +Use the `Reasoning` component when your model outputs thinking content as a single block or continuous stream (Deepseek R1, Claude with extended thinking, etc.). + +If your model outputs discrete, labeled steps (search queries, tool calls, distinct thought stages), consider using the [Chain of Thought](/components/chain-of-thought) component instead for a more structured visual representation. + +## Features + +- Automatically opens when streaming content and closes when finished +- Manual toggle control for user interaction +- Smooth animations and transitions powered by Radix UI +- Visual streaming indicator with pulsing animation +- Composable architecture with separate trigger and content components +- Built with accessibility in mind including keyboard navigation +- Responsive design that works across different screen sizes +- Seamlessly integrates with both light and dark themes +- Built on top of shadcn/ui Collapsible primitives +- TypeScript support with proper type definitions + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `isStreaming` | `boolean` | `false` | Whether the reasoning is currently streaming (auto-opens and closes the panel). | +| `open` | `boolean` | - | Controlled open state. | +| `defaultOpen` | `boolean` | `true` | Default open state when uncontrolled. | +| `onOpenChange` | `(open: boolean) => void` | - | Callback when open state changes. | +| `duration` | `number` | - | Duration in seconds to display (can be controlled externally). | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `getThinkingMessage` | `(isStreaming: boolean, duration?: number) => ReactNode` | - | Optional function to customize the thinking message. Receives isStreaming and duration parameters. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CollapsibleTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `string` | Required | The reasoning text to display (rendered via Streamdown). | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying CollapsibleContent component. | + +## Hooks + +### `useReasoning` + +Access the reasoning context from child components. + +```tsx +const { isStreaming, isOpen, setIsOpen, duration } = useReasoning(); +``` + +Returns: + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `isStreaming` | `boolean` | - | Whether reasoning is currently streaming. | +| `isOpen` | `boolean` | - | Whether the reasoning panel is open. | +| `setIsOpen` | `(open: boolean) => void` | - | Function to set the open state. | +| `duration` | `number | undefined` | - | Duration in seconds (undefined while streaming). | diff --git a/.agents/skills/ai-elements/references/sandbox.md b/.agents/skills/ai-elements/references/sandbox.md new file mode 100644 index 0000000..adc7ee1 --- /dev/null +++ b/.agents/skills/ai-elements/references/sandbox.md @@ -0,0 +1,129 @@ +# Sandbox + +A collapsible container for displaying AI-generated code and output in chat interfaces. + +The `Sandbox` component provides a structured way to display AI-generated code alongside its execution output in chat conversations. It features a collapsible container with status indicators and tabbed navigation between code and output views. It's designed to be used with `CodeBlock` for displaying code and `StackTrace` for displaying errors. + +See `scripts/sandbox.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add sandbox +``` + +## Features + +- Collapsible container with smooth animations +- Status badges showing execution state (Pending, Running, Completed, Error) +- Tabs for Code and Output views +- Syntax-highlighted code display +- Copy button for easy code sharing +- Works with AI SDK tool state patterns + +## Usage with AI SDK + +The Sandbox component integrates with the AI SDK's tool state to show code generation progress: + +```tsx title="components/code-sandbox.tsx" +"use client"; + +import type { ToolUIPart } from "ai"; +import { + Sandbox, + SandboxContent, + SandboxHeader, + SandboxTabContent, + SandboxTabs, + SandboxTabsBar, + SandboxTabsList, + SandboxTabsTrigger, +} from "@/components/ai-elements/sandbox"; +import { CodeBlock } from "@/components/ai-elements/code-block"; + +type CodeSandboxProps = { + toolPart: ToolUIPart; +}; + +export const CodeSandbox = ({ toolPart }: CodeSandboxProps) => { + const code = toolPart.input?.code ?? ""; + const output = toolPart.output?.logs ?? ""; + + return ( + + + + + + + Code + Output + + + + + + + + + + + + ); +}; +``` + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | `string` | `undefined` | The title displayed in the header (e.g., filename). | +| `state` | `ToolUIPart[` | Required | The current execution state, used to display the appropriate status badge. | +| `className` | `string` | - | Additional CSS classes for the header. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleContent. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying Tabs component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying TabsList component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying TabsTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying TabsContent component. | diff --git a/.agents/skills/ai-elements/references/schema-display.md b/.agents/skills/ai-elements/references/schema-display.md new file mode 100644 index 0000000..0ed1947 --- /dev/null +++ b/.agents/skills/ai-elements/references/schema-display.md @@ -0,0 +1,102 @@ +# Schema Display + +Display REST API endpoint documentation with parameters, request/response bodies. + +The `SchemaDisplay` component visualizes REST API endpoints with HTTP methods, paths, parameters, and request/response schemas. + +See `scripts/schema-display.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add schema-display +``` + +## Features + +- Color-coded HTTP methods +- Path parameter highlighting +- Collapsible parameters section +- Request/response body schemas +- Nested object property display +- Required field indicators + +## Method Colors + +| Method | Color | +| -------- | ------ | +| `GET` | Green | +| `POST` | Blue | +| `PUT` | Orange | +| `PATCH` | Yellow | +| `DELETE` | Red | + +## Examples + +### Basic Usage + +See `scripts/schema-display-basic.tsx` for this example. + +### With Parameters + +See `scripts/schema-display-params.tsx` for this example. + +### With Request/Response Bodies + +See `scripts/schema-display-body.tsx` for this example. + +### Nested Properties + +See `scripts/schema-display-nested.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `method` | `unknown` | - | HTTP method. | +| `path` | `string` | - | API endpoint path. | +| `description` | `string` | - | Endpoint description. | +| `parameters` | `SchemaParameter[]` | - | URL/query parameters. | +| `requestBody` | `SchemaProperty[]` | - | Request body properties. | +| `responseBody` | `SchemaProperty[]` | - | Response body properties. | + +### `SchemaParameter` + +```tsx +interface SchemaParameter { + name: string; + type: string; + required?: boolean; + description?: string; + location?: "path" | "query" | "header"; +} +``` + +### `SchemaProperty` + +```tsx +interface SchemaProperty { + name: string; + type: string; + required?: boolean; + description?: string; + properties?: SchemaProperty[]; // For objects + items?: SchemaProperty; // For arrays +} +``` + +### Subcomponents + +- `SchemaDisplayHeader` - Header container +- `SchemaDisplayMethod` - Color-coded method badge +- `SchemaDisplayPath` - Path with highlighted parameters +- `SchemaDisplayDescription` - Description text +- `SchemaDisplayContent` - Content container +- `SchemaDisplayParameters` - Collapsible parameters section +- `SchemaDisplayParameter` - Individual parameter +- `SchemaDisplayRequest` - Collapsible request body +- `SchemaDisplayResponse` - Collapsible response body +- `SchemaDisplayProperty` - Schema property (recursive) +- `SchemaDisplayExample` - Code example block diff --git a/.agents/skills/ai-elements/references/shimmer.md b/.agents/skills/ai-elements/references/shimmer.md new file mode 100644 index 0000000..3af11dd --- /dev/null +++ b/.agents/skills/ai-elements/references/shimmer.md @@ -0,0 +1,48 @@ +# Shimmer + +An animated text shimmer component for creating eye-catching loading states and progressive reveal effects. + +The `Shimmer` component provides an animated shimmer effect that sweeps across text, perfect for indicating loading states, progressive reveals, or drawing attention to dynamic content in AI applications. + +See `scripts/shimmer.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add shimmer +``` + +## Features + +- Smooth animated shimmer effect using CSS gradients and Framer Motion +- Customizable animation duration and spread +- Polymorphic component - render as any HTML element via the `as` prop +- Automatic spread calculation based on text length +- Theme-aware styling using CSS custom properties +- Infinite looping animation with linear easing +- TypeScript support with proper type definitions +- Memoized for optimal performance +- Responsive and accessible design +- Uses `text-transparent` with background-clip for crisp text rendering + +## Examples + +### Different Durations + +See `scripts/shimmer-duration.tsx` for this example. + +### Custom Elements + +See `scripts/shimmer-elements.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `string` | - | The text content to apply the shimmer effect to. | +| `as` | `ElementType` | - | The HTML element or React component to render. | +| `className` | `string` | - | Additional CSS classes to apply to the component. | +| `duration` | `number` | `2` | The duration of the shimmer animation in seconds. | +| `spread` | `number` | `2` | The spread multiplier for the shimmer gradient, multiplied by text length. | diff --git a/.agents/skills/ai-elements/references/snippet.md b/.agents/skills/ai-elements/references/snippet.md new file mode 100644 index 0000000..9e9cf4c --- /dev/null +++ b/.agents/skills/ai-elements/references/snippet.md @@ -0,0 +1,64 @@ +# Snippet + +Lightweight inline code display for terminal commands and short code references. + +The `Snippet` component provides a lightweight way to display terminal commands and short code snippets with copy functionality. Built on top of InputGroup, it's designed for brief code references in text. + +See `scripts/snippet.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add snippet +``` + +## Features + +- Composable architecture with InputGroup +- Optional prefix text (e.g., `$` for terminal commands) +- Built-in copy button +- Compact design for chat/markdown + +## Examples + +### Without Prefix + +See `scripts/snippet-plain.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `code` | `string` | Required | The code content to display. | +| `children` | `React.ReactNode` | - | Child elements like SnippetAddon, SnippetInput, etc. | +| `...props` | `React.ComponentProps` | - | Spread to the InputGroup component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the InputGroupAddon component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Spread to the InputGroupText component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `Omit, ` | - | Spread to the InputGroupInput component. Value and readOnly are set automatically. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onCopy` | `() => void` | - | Callback fired after a successful copy. | +| `onError` | `(error: Error) => void` | - | Callback fired if copying fails. | +| `timeout` | `number` | `2000` | How long to show the copied state (ms). | +| `children` | `React.ReactNode` | - | Custom button content. | +| `...props` | `React.ComponentProps` | - | Spread to the InputGroupButton component. | diff --git a/.agents/skills/ai-elements/references/sources.md b/.agents/skills/ai-elements/references/sources.md new file mode 100644 index 0000000..e83671a --- /dev/null +++ b/.agents/skills/ai-elements/references/sources.md @@ -0,0 +1,209 @@ +# Sources + +A component that allows a user to view the sources or citations used to generate a response. + +The `Sources` component allows a user to view the sources or citations used to generate a response. + +See `scripts/sources.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add sources +``` + +## Usage with AI SDK + +Build a simple web search agent with Perplexity Sonar. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { useChat } from "@ai-sdk/react"; +import { + Source, + Sources, + SourcesContent, + SourcesTrigger, +} from "@/components/ai-elements/sources"; +import { + PromptInput, + type PromptInputMessage, + PromptInputTextarea, + PromptInputSubmit, +} from "@/components/ai-elements/prompt-input"; +import { + Conversation, + ConversationContent, + ConversationScrollButton, +} from "@/components/ai-elements/conversation"; +import { + Message, + MessageContent, + MessageResponse, +} from "@/components/ai-elements/message"; +import { useState } from "react"; +import { DefaultChatTransport } from "ai"; + +const SourceDemo = () => { + const [input, setInput] = useState(""); + const { messages, sendMessage, status } = useChat({ + transport: new DefaultChatTransport({ + api: "/api/sources", + }), + }); + + const handleSubmit = (message: PromptInputMessage) => { + if (message.text.trim()) { + sendMessage({ text: message.text }); + setInput(""); + } + }; + + return ( +
+
+
+ + + {messages.map((message) => ( +
+ {message.role === "assistant" && ( + + part.type === "source-url" + ).length + } + /> + {message.parts.map((part, i) => { + switch (part.type) { + case "source-url": + return ( + + + + ); + } + })} + + )} + + + {message.parts.map((part, i) => { + switch (part.type) { + case "text": + return ( + + {part.text} + + ); + default: + return null; + } + })} + + +
+ ))} +
+ +
+
+ + + setInput(e.currentTarget.value)} + className="pr-12" + /> + + +
+
+ ); +}; + +export default SourceDemo; +``` + +Add the following route to your backend: + +```tsx title="api/chat/route.ts" +import { convertToModelMessages, streamText, UIMessage } from "ai"; +import { perplexity } from "@ai-sdk/perplexity"; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { messages }: { messages: UIMessage[] } = await req.json(); + + const result = streamText({ + model: "perplexity/sonar", + system: + "You are a helpful assistant. Keep your responses short (< 100 words) unless you are asked for more details. ALWAYS USE SEARCH.", + messages: await convertToModelMessages(messages), + }); + + return result.toUIMessageStreamResponse({ + sendSources: true, + }); +} +``` + +## Features + +- Collapsible component that allows a user to view the sources or citations used to generate a response +- Customizable trigger and content components +- Support for custom sources or citations +- Responsive design with mobile-friendly controls +- Clean, modern styling with customizable themes + +## Examples + +### Custom rendering + +See `scripts/sources-custom.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `count` | `number` | Required | The number of sources to display in the trigger. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the content container. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.AnchorHTMLAttributes` | - | Any other props are spread to the anchor element. | diff --git a/.agents/skills/ai-elements/references/speech-input.md b/.agents/skills/ai-elements/references/speech-input.md new file mode 100644 index 0000000..f80e453 --- /dev/null +++ b/.agents/skills/ai-elements/references/speech-input.md @@ -0,0 +1,163 @@ +# Speech Input + +A button component that captures voice input and converts it to text, with cross-browser support. + +The `SpeechInput` component provides an easy-to-use interface for capturing voice input in your application. It uses the Web Speech API for real-time transcription in supported browsers (Chrome, Edge), and falls back to MediaRecorder with an external transcription service for browsers that don't support Web Speech API (Firefox, Safari). + +See `scripts/speech-input.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add speech-input +``` + +## Features + +- Built on Web Speech API (SpeechRecognition) with MediaRecorder fallback +- Cross-browser support (Chrome, Edge, Firefox, Safari) +- Continuous speech recognition with interim results +- Visual feedback with pulse animation when listening +- Loading state during transcription processing +- Automatic browser compatibility detection +- Final transcript extraction and callbacks +- Error handling and automatic state management +- Extends shadcn/ui Button component +- Full TypeScript support + +## Props + +### `` + +The component extends the shadcn/ui Button component, so all Button props are available. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onTranscriptionChange` | `(text: string) => void` | - | Callback fired when final transcription text is available. Only fires for completed phrases, not interim results. | +| `onAudioRecorded` | `(audioBlob: Blob) => Promise` | - | Callback for MediaRecorder fallback. Required for Firefox/Safari support. Receives recorded audio blob and should return transcribed text from an external service (e.g., OpenAI Whisper). | +| `lang` | `string` | - | Language for speech recognition. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Button component, including variant, size, disabled, etc. | + +## Behavior + +### Speech Recognition Modes + +The component automatically detects browser capabilities and uses the best available method: + +| Browser | Mode | Behavior | +| --------------- | -------------- | ------------------------------------------------------ | +| Chrome, Edge | Web Speech API | Real-time transcription, no server required | +| Firefox, Safari | MediaRecorder | Records audio, sends to external transcription service | +| Unsupported | Disabled | Button is disabled | + +### Web Speech API Mode (Chrome, Edge) + +Uses the Web Speech API with the following configuration: + +- **Continuous**: Set to `true` to keep recognition active until manually stopped +- **Interim Results**: Set to `true` to receive partial results during speech +- **Language**: Configurable via `lang` prop, defaults to `"en-US"` + +### MediaRecorder Mode (Firefox, Safari) + +When the Web Speech API is unavailable, the component falls back to recording audio: + +1. Records audio using `MediaRecorder` API +2. On stop, creates an audio blob (`audio/webm`) +3. Calls `onAudioRecorded` with the blob +4. Waits for transcription result +5. Passes result to `onTranscriptionChange` + +**Note**: The `onAudioRecorded` prop is required for this mode to work. Without it, the button will be disabled in Firefox/Safari. + +### Transcription Processing + +The component only calls `onTranscriptionChange` with **final transcripts**. Interim results (Web Speech API) are ignored to prevent incomplete text from being processed. + +### Visual States + +- **Default State**: Standard button appearance with microphone icon +- **Listening State**: Pulsing animation with accent colors to indicate active listening +- **Processing State**: Loading spinner while waiting for transcription (MediaRecorder mode) +- **Disabled State**: Button is disabled when no API is available or required props are missing + +### Lifecycle + +1. **Mount**: Detects available APIs and initializes appropriate mode +2. **Click**: Toggles between listening/recording and stopped states +3. **Stop (MediaRecorder)**: Processes audio and waits for transcription +4. **Unmount**: Stops recognition/recording and releases microphone + +## Browser Support + +The component provides cross-browser support through a two-tier system: + +| Browser | API Used | Requirements | +| ------- | -------------- | ---------------------- | +| Chrome | Web Speech API | None | +| Edge | Web Speech API | None | +| Firefox | MediaRecorder | `onAudioRecorded` prop | +| Safari | MediaRecorder | `onAudioRecorded` prop | + +For full cross-browser support, provide the `onAudioRecorded` callback that sends audio to a transcription service like OpenAI Whisper, Google Cloud Speech-to-Text, or AssemblyAI. + +## Accessibility + +- Uses semantic button element via shadcn/ui Button +- Visual feedback for listening state +- Keyboard accessible (can be triggered with Space/Enter) +- Screen reader friendly with proper button semantics + +## Usage with MediaRecorder Fallback + +To support Firefox and Safari, provide an `onAudioRecorded` callback that sends audio to a transcription service: + +```tsx +const handleAudioRecorded = async (audioBlob: Blob): Promise => { + const formData = new FormData(); + formData.append("file", audioBlob, "audio.webm"); + formData.append("model", "whisper-1"); + + const response = await fetch( + "https://api.openai.com/v1/audio/transcriptions", + { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, + }, + body: formData, + } + ); + + const data = await response.json(); + return data.text; +}; + + console.log(text)} + onAudioRecorded={handleAudioRecorded} +/>; +``` + +## Notes + +- Requires a secure context (HTTPS or localhost) +- Browser may prompt user for microphone permission +- Only final transcripts trigger the `onTranscriptionChange` callback +- Language is configurable via the `lang` prop +- Continuous recognition continues until button is clicked again +- Errors are logged to console and automatically stop recognition/recording +- MediaRecorder fallback requires the `onAudioRecorded` prop to be provided +- Audio is recorded in `audio/webm` format for the MediaRecorder fallback + +## TypeScript + +The component includes full TypeScript definitions for the Web Speech API: + +- `SpeechRecognition` +- `SpeechRecognitionEvent` +- `SpeechRecognitionResult` +- `SpeechRecognitionAlternative` +- `SpeechRecognitionErrorEvent` + +These types are properly declared for both standard and webkit-prefixed implementations. diff --git a/.agents/skills/ai-elements/references/stack-trace.md b/.agents/skills/ai-elements/references/stack-trace.md new file mode 100644 index 0000000..cff6ff5 --- /dev/null +++ b/.agents/skills/ai-elements/references/stack-trace.md @@ -0,0 +1,224 @@ +# Stack Trace + +Displays formatted JavaScript/Node.js error stack traces with syntax highlighting and collapsible frames. + +The `StackTrace` component displays formatted JavaScript/Node.js error stack traces with clickable file paths, internal frame dimming, and collapsible content. + +See `scripts/stack-trace.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add stack-trace +``` + +## Usage with AI SDK + +Build an error display tool that shows stack traces from AI-generated code using the [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { useChat } from "@ai-sdk/react"; +import { + StackTrace, + StackTraceHeader, + StackTraceError, + StackTraceErrorType, + StackTraceErrorMessage, + StackTraceActions, + StackTraceCopyButton, + StackTraceExpandButton, + StackTraceContent, + StackTraceFrames, +} from "@/components/ai-elements/stack-trace"; + +export default function Page() { + const { messages } = useChat({ + api: "/api/run-code", + }); + + return ( +
+ {messages.map((message) => { + const toolInvocations = message.parts?.filter( + (part) => part.type === "tool-invocation" + ); + + return toolInvocations?.map((tool) => { + if (tool.toolName === "runCode" && tool.result?.error) { + return ( + + + + + + + + + + + + + + + + ); + } + return null; + }); + })} +
+ ); +} +``` + +Add the following route to your backend: + +```tsx title="api/run-code/route.ts" +import { streamText, tool } from "ai"; +import { z } from "zod"; + +export async function POST(req: Request) { + const { messages } = await req.json(); + + const result = streamText({ + model: "openai/gpt-4o", + messages, + tools: { + runCode: tool({ + description: "Execute JavaScript code and return any errors", + parameters: z.object({ + code: z.string(), + }), + execute: async ({ code }) => { + try { + // Execute code in sandbox + eval(code); + return { success: true }; + } catch (error) { + return { error: (error as Error).stack }; + } + }, + }), + }, + }); + + return result.toDataStreamResponse(); +} +``` + +## Features + +- Parses standard JavaScript/Node.js stack trace format +- Highlights error type in red +- Dims internal frames (node_modules, node: paths) +- Collapsible content with smooth animation +- Copy full stack trace to clipboard +- Clickable file paths with line/column numbers + +## Examples + +### Collapsed by Default + +See `scripts/stack-trace-collapsed.tsx` for this example. + +### Hide Internal Frames + +See `scripts/stack-trace-no-internal.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `trace` | `string` | - | The raw stack trace string to parse and display. | +| `open` | `boolean` | - | Controlled open state. | +| `defaultOpen` | `boolean` | `false` | Whether the content is expanded by default. | +| `onOpenChange` | `(open: boolean) => void` | - | Callback when open state changes. | +| `onFilePathClick` | `(path: string, line?: number, column?: number) => void` | - | Callback when a file path is clicked. Receives the file path, line number, and column number. | +| `children` | `React.ReactNode` | - | Child elements (StackTraceHeader, StackTraceContent, etc.). | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Header content (typically StackTraceError and StackTraceActions). | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Error content (typically StackTraceErrorType and StackTraceErrorMessage). | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom content. Defaults to the parsed error type (e.g., | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Custom content. Defaults to the parsed error message. | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `children` | `React.ReactNode` | - | Action buttons (typically StackTraceCopyButton and StackTraceExpandButton). | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onCopy` | `() => void` | - | Callback fired after a successful copy. | +| `onError` | `(error: Error) => void` | - | Callback fired if copying fails. | +| `timeout` | `number` | `2000` | How long to show the copied state (ms). | +| `children` | `React.ReactNode` | - | Custom content for the button. Defaults to copy/check icons. | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the container div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `maxHeight` | `number` | `400` | Maximum height of the content area. Enables scrolling when content exceeds this height. | +| `children` | `React.ReactNode` | - | Content to display (typically StackTraceFrames). | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleContent. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `showInternalFrames` | `boolean` | `true` | Whether to show internal frames (node_modules, node: paths). | +| `className` | `string` | - | Additional CSS classes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the container div. | diff --git a/.agents/skills/ai-elements/references/suggestion.md b/.agents/skills/ai-elements/references/suggestion.md new file mode 100644 index 0000000..08cc3ae --- /dev/null +++ b/.agents/skills/ai-elements/references/suggestion.md @@ -0,0 +1,124 @@ +# Suggestion + +A suggestion component that displays a horizontal row of clickable suggestions for user interaction. + +The `Suggestion` component displays a horizontal row of clickable suggestions for user interaction. + +See `scripts/suggestion.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add suggestion +``` + +## Usage with AI SDK + +Build a simple input with suggestions users can click to send a message to the LLM. + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { + PromptInput, + type PromptInputMessage, + PromptInputTextarea, + PromptInputSubmit, +} from "@/components/ai-elements/prompt-input"; +import { Suggestion, Suggestions } from "@/components/ai-elements/suggestion"; +import { useState } from "react"; +import { useChat } from "@ai-sdk/react"; + +const suggestions = [ + "Can you explain how to play tennis?", + "What is the weather in Tokyo?", + "How do I make a really good fish taco?", +]; + +const SuggestionDemo = () => { + const [input, setInput] = useState(""); + const { sendMessage, status } = useChat(); + + const handleSubmit = (message: PromptInputMessage) => { + if (message.text.trim()) { + sendMessage({ text: message.text }); + setInput(""); + } + }; + + const handleSuggestionClick = (suggestion: string) => { + sendMessage({ text: suggestion }); + }; + + return ( +
+
+
+ + {suggestions.map((suggestion) => ( + + ))} + + + setInput(e.currentTarget.value)} + className="pr-12" + /> + + +
+
+
+ ); +}; + +export default SuggestionDemo; +``` + +## Features + +- Horizontal row of clickable suggestion buttons +- Customizable styling with variant and size options +- Flexible layout that wraps suggestions on smaller screens +- onClick callback that emits the selected suggestion string +- Support for both individual suggestions and suggestion lists +- Clean, modern styling with hover effects +- Responsive design with mobile-friendly touch targets +- TypeScript support with proper type definitions + +## Examples + +### Usage with AI Input + +See `scripts/suggestion-input.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying ScrollArea component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `suggestion` | `string` | Required | The suggestion string to display and emit on click. | +| `onClick` | `(suggestion: string) => void` | - | Callback fired when the suggestion is clicked. | +| `...props` | `Omit, ` | - | Any other props are spread to the underlying shadcn/ui Button component. | diff --git a/.agents/skills/ai-elements/references/task.md b/.agents/skills/ai-elements/references/task.md new file mode 100644 index 0000000..70e055e --- /dev/null +++ b/.agents/skills/ai-elements/references/task.md @@ -0,0 +1,222 @@ +# Task + +A collapsible task list component for displaying AI workflow progress, with status indicators and optional descriptions. + +The `Task` component provides a structured way to display task lists or workflow progress with collapsible details, status indicators, and progress tracking. It consists of a main `Task` container with `TaskTrigger` for the clickable header and `TaskContent` for the collapsible content area. + +See `scripts/task.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add task +``` + +## Usage with AI SDK + +Build a mock async programming agent using [`experimental_generateObject`](/docs/reference/ai-sdk-ui/use-object). + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { experimental_useObject as useObject } from "@ai-sdk/react"; +import { + Task, + TaskItem, + TaskItemFile, + TaskTrigger, + TaskContent, +} from "@/components/ai-elements/task"; +import { Button } from "@/components/ui/button"; +import { tasksSchema } from "@/app/api/task/route"; +import { + SiReact, + SiTypescript, + SiJavascript, + SiCss, + SiHtml5, + SiJson, + SiMarkdown, +} from "@icons-pack/react-simple-icons"; + +const iconMap = { + react: { component: SiReact, color: "#149ECA" }, + typescript: { component: SiTypescript, color: "#3178C6" }, + javascript: { component: SiJavascript, color: "#F7DF1E" }, + css: { component: SiCss, color: "#1572B6" }, + html: { component: SiHtml5, color: "#E34F26" }, + json: { component: SiJson, color: "#000000" }, + markdown: { component: SiMarkdown, color: "#000000" }, +}; + +const TaskDemo = () => { + const { object, submit, isLoading } = useObject({ + api: "/api/agent", + schema: tasksSchema, + }); + + const handleSubmit = (taskType: string) => { + submit({ prompt: taskType }); + }; + + const renderTaskItem = (item: any, index: number) => { + if (item?.type === "file" && item.file) { + const iconInfo = iconMap[item.file.icon as keyof typeof iconMap]; + if (iconInfo) { + const IconComponent = iconInfo.component; + return ( + + {item.text} + + + {item.file.name} + + + ); + } + } + return item?.text || ""; + }; + + return ( +
+
+
+ +
+ +
+ {isLoading && !object && ( +
Generating tasks...
+ )} + + {object?.tasks?.map((task: any, taskIndex: number) => ( + + + + {task.items?.map((item: any, itemIndex: number) => ( + + {renderTaskItem(item, itemIndex)} + + ))} + + + ))} +
+
+
+ ); +}; + +export default TaskDemo; +``` + +Add the following route to your backend: + +```ts title="app/api/agent.ts" +import { streamObject } from "ai"; +import { z } from "zod"; + +export const taskItemSchema = z.object({ + type: z.enum(["text", "file"]), + text: z.string(), + file: z + .object({ + name: z.string(), + icon: z.string(), + color: z.string().optional(), + }) + .optional(), +}); + +export const taskSchema = z.object({ + title: z.string(), + items: z.array(taskItemSchema), + status: z.enum(["pending", "in_progress", "completed"]), +}); + +export const tasksSchema = z.object({ + tasks: z.array(taskSchema), +}); + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { prompt } = await req.json(); + + const result = streamObject({ + model: "openai/gpt-4o", + schema: tasksSchema, + prompt: `You are an AI assistant that generates realistic development task workflows. Generate a set of tasks that would occur during ${prompt}. + + Each task should have: + - A descriptive title + - Multiple task items showing the progression + - Some items should be plain text, others should reference files + - Use realistic file names and appropriate file types + - Status should progress from pending to in_progress to completed + + For file items, use these icon types: 'react', 'typescript', 'javascript', 'css', 'html', 'json', 'markdown' + + Generate 3-4 tasks total, with 4-6 items each.`, + }); + + return result.toTextStreamResponse(); +} +``` + +## Features + +- Visual icons for pending, in-progress, completed, and error states +- Expandable content for task descriptions and additional information +- Built-in progress counter showing completed vs total tasks +- Optional progressive reveal of tasks with customizable timing +- Support for custom content within task items +- Full type safety with proper TypeScript definitions +- Keyboard navigation and screen reader support + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `defaultOpen` | `boolean` | `true` | Whether the task is open by default. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the root Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | `string` | Required | The title of the task that will be displayed in the trigger. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | diff --git a/.agents/skills/ai-elements/references/terminal.md b/.agents/skills/ai-elements/references/terminal.md new file mode 100644 index 0000000..44c9c45 --- /dev/null +++ b/.agents/skills/ai-elements/references/terminal.md @@ -0,0 +1,103 @@ +# Terminal + +Display streaming console output with full ANSI color support. + +The `Terminal` component displays console output with ANSI color support, streaming indicators, and auto-scroll functionality. + +See `scripts/terminal.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add terminal +``` + +## Features + +- Full ANSI color support (256 colors, bold, italic, underline) +- Streaming mode with cursor animation +- Auto-scroll to latest output +- Copy output to clipboard +- Clear button support +- Dark terminal theme + +## ANSI Support + +The Terminal uses `ansi-to-react` to parse ANSI escape codes: + +```bash +\x1b[32m✓\x1b[0m Success # Green checkmark +\x1b[31m✗\x1b[0m Error # Red X +\x1b[33mwarn\x1b[0m Warning # Yellow text +\x1b[1mBold\x1b[0m # Bold text +``` + +## Examples + +### Basic Usage + +See `scripts/terminal-basic.tsx` for this example. + +### Streaming Mode + +See `scripts/terminal-streaming.tsx` for this example. + +### With Clear Button + +See `scripts/terminal-clear.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `output` | `string` | - | Terminal output text (supports ANSI codes). | +| `isStreaming` | `boolean` | `false` | Show streaming indicator. | +| `autoScroll` | `boolean` | `true` | Auto-scroll to bottom on new output. | +| `onClear` | `() => void` | - | Callback to clear output (enables clear button). | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onCopy` | `() => void` | - | Callback after successful copy. | +| `onError` | `(error: Error) => void` | - | Callback if copying fails. | +| `timeout` | `number` | `2000` | Duration to show copied state (ms). | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | diff --git a/.agents/skills/ai-elements/references/test-results.md b/.agents/skills/ai-elements/references/test-results.md new file mode 100644 index 0000000..74fd4da --- /dev/null +++ b/.agents/skills/ai-elements/references/test-results.md @@ -0,0 +1,157 @@ +# Test Results + +Display test suite results with pass/fail/skip status and error details. + +The `TestResults` component displays test suite results including summary statistics, progress, individual tests, and error details. + +See `scripts/test-results.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add test-results +``` + +## Features + +- Summary statistics (passed/failed/skipped) +- Progress bar visualization +- Collapsible test suites +- Individual test status and duration +- Error messages with stack traces +- Color-coded status indicators + +## Status Colors + +| Status | Color | Use Case | +| --------- | --------------- | ---------------- | +| `passed` | Green | Test succeeded | +| `failed` | Red | Test failed | +| `skipped` | Yellow | Test skipped | +| `running` | Blue (animated) | Test in progress | + +## Examples + +### Basic Usage + +See `scripts/test-results-basic.tsx` for this example. + +### With Test Suites + +See `scripts/test-results-suites.tsx` for this example. + +### With Error Details + +See `scripts/test-results-errors.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `summary` | `unknown` | - | Test results summary. | +| `className` | `string` | - | Additional CSS classes. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `name` | `string` | - | Suite name. | +| `status` | `unknown` | - | Overall suite status. | +| `defaultOpen` | `boolean` | - | Initially expanded. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `name` | `string` | - | Test name. | +| `status` | `unknown` | - | Test status. | +| `duration` | `number` | - | Test duration in ms. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `passed` | `number` | `0` | Number of passed tests. | +| `failed` | `number` | `0` | Number of failed tests. | +| `skipped` | `number` | `0` | Number of skipped tests. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleContent component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the span element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the div element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the p element. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the pre element. | diff --git a/.agents/skills/ai-elements/references/tool.md b/.agents/skills/ai-elements/references/tool.md new file mode 100644 index 0000000..bf44e08 --- /dev/null +++ b/.agents/skills/ai-elements/references/tool.md @@ -0,0 +1,285 @@ +# Tool + +A collapsible component for displaying tool invocation details in AI chatbot interfaces. + +The `Tool` component displays a collapsible interface for showing/hiding tool details. It is designed to take the `ToolUIPart` type from the AI SDK and display it in a collapsible interface. + +See `scripts/tool.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add tool +``` + +## Usage in AI SDK + +Build a simple stateful weather app that renders the last message in a tool using [`useChat`](/docs/reference/ai-sdk-ui/use-chat). + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { useChat } from "@ai-sdk/react"; +import { DefaultChatTransport, type ToolUIPart } from "ai"; +import { Button } from "@/components/ui/button"; +import { MessageResponse } from "@/components/ai-elements/message"; +import { + Tool, + ToolContent, + ToolHeader, + ToolInput, + ToolOutput, +} from "@/components/ai-elements/tool"; + +type WeatherToolInput = { + location: string; + units: "celsius" | "fahrenheit"; +}; + +type WeatherToolOutput = { + location: string; + temperature: string; + conditions: string; + humidity: string; + windSpeed: string; + lastUpdated: string; +}; + +type WeatherToolUIPart = ToolUIPart<{ + fetch_weather_data: { + input: WeatherToolInput; + output: WeatherToolOutput; + }; +}>; + +const Example = () => { + const { messages, sendMessage, status } = useChat({ + transport: new DefaultChatTransport({ + api: "/api/weather", + }), + }); + + const handleWeatherClick = () => { + sendMessage({ text: "Get weather data for San Francisco in fahrenheit" }); + }; + + const latestMessage = messages[messages.length - 1]; + const weatherTool = latestMessage?.parts?.find( + (part) => part.type === "tool-fetch_weather_data" + ) as WeatherToolUIPart | undefined; + + return ( +
+
+
+ + + {weatherTool && ( + + + + + + {formatWeatherResult(weatherTool.output)} + + } + errorText={weatherTool.errorText} + /> + + + )} +
+
+
+ ); +}; + +function formatWeatherResult(result: WeatherToolOutput): string { + return `**Weather for ${result.location}** + +**Temperature:** ${result.temperature} +**Conditions:** ${result.conditions} +**Humidity:** ${result.humidity} +**Wind Speed:** ${result.windSpeed} + +*Last updated: ${result.lastUpdated}*`; +} + +export default Example; +``` + +Add the following route to your backend: + +```ts title="app/api/weather/route.tsx" +import { streamText, UIMessage, convertToModelMessages } from "ai"; +import { z } from "zod"; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const { messages }: { messages: UIMessage[] } = await req.json(); + + const result = streamText({ + model: "openai/gpt-4o", + messages: await convertToModelMessages(messages), + tools: { + fetch_weather_data: { + description: "Fetch weather information for a specific location", + parameters: z.object({ + location: z + .string() + .describe("The city or location to get weather for"), + units: z + .enum(["celsius", "fahrenheit"]) + .default("celsius") + .describe("Temperature units"), + }), + inputSchema: z.object({ + location: z.string(), + units: z.enum(["celsius", "fahrenheit"]).default("celsius"), + }), + execute: async ({ location, units }) => { + await new Promise((resolve) => setTimeout(resolve, 1500)); + + const temp = + units === "celsius" + ? Math.floor(Math.random() * 35) + 5 + : Math.floor(Math.random() * 63) + 41; + + return { + location, + temperature: `${temp}°${units === "celsius" ? "C" : "F"}`, + conditions: "Sunny", + humidity: `12%`, + windSpeed: `35 ${units === "celsius" ? "km/h" : "mph"}`, + lastUpdated: new Date().toLocaleString(), + }; + }, + }, + }, + }); + + return result.toUIMessageStreamResponse(); +} +``` + +## Features + +- Collapsible interface for showing/hiding tool details +- Visual status indicators with icons and badges +- Support for multiple tool execution states (pending, running, completed, error) +- Formatted parameter display with JSON syntax highlighting +- Result and error handling with appropriate styling +- Composable structure for flexible layouts +- Accessible keyboard navigation and screen reader support +- Consistent styling that matches your design system +- Auto-opens completed tools by default for better UX + +## Examples + +### Input Streaming (Pending) + +Shows a tool in its initial state while parameters are being processed. + +See `scripts/tool-input-streaming.tsx` for this example. + +### Input Available (Running) + +Shows a tool that's actively executing with its parameters. + +See `scripts/tool-input-available.tsx` for this example. + +### Output Available (Completed) + +Shows a completed tool with successful results. Opens by default to show the results. In this instance, the output is a JSON object, so we can use the `CodeBlock` component to display it. + +See `scripts/tool-output-available.tsx` for this example. + +### Output Error + +Shows a tool that encountered an error during execution. Opens by default to display the error. + +See `scripts/tool-output-error.tsx` for this example. + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the root Collapsible component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | `string` | - | Custom title to display instead of the derived tool name. | +| `type` | `ToolUIPart[` | Required | The type/name of the tool. | +| `state` | `ToolUIPart[` | Required | The current state of the tool (input-streaming, input-available, output-available, or output-error). | +| `toolName` | `string` | - | Required when type is | +| `className` | `string` | - | Additional CSS classes to apply to the header. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleTrigger. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the CollapsibleContent. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `input` | `ToolUIPart[` | - | The input parameters passed to the tool, displayed as formatted JSON. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `output` | `React.ReactNode` | - | The output/result of the tool execution. | +| `errorText` | `ToolUIPart[` | - | An error message if the tool execution failed. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the underlying div. | + +## Type Exports + +### `ToolPart` + +Union type representing both static and dynamic tool UI parts. + +```tsx +type ToolPart = ToolUIPart | DynamicToolUIPart; +``` + +## Utilities + +### `getStatusBadge` + +Returns a Badge component with icon and label based on tool state. + +```tsx +import { getStatusBadge } from "@/components/ai-elements/tool"; + +// Returns a Badge with appropriate icon and label +const badge = getStatusBadge("output-available"); +``` + +Supported states: + +- `input-streaming` - "Pending" +- `input-available` - "Running" +- `approval-requested` - "Awaiting Approval" +- `approval-responded` - "Responded" +- `output-available` - "Completed" +- `output-error` - "Error" +- `output-denied` - "Denied" diff --git a/.agents/skills/ai-elements/references/toolbar.md b/.agents/skills/ai-elements/references/toolbar.md new file mode 100644 index 0000000..20b2542 --- /dev/null +++ b/.agents/skills/ai-elements/references/toolbar.md @@ -0,0 +1,32 @@ +# Toolbar + +A styled toolbar component for React Flow nodes with flexible positioning and custom actions. + +The `Toolbar` component provides a positioned toolbar that attaches to nodes in React Flow canvases. It features modern card styling with backdrop blur and flexbox layout for action buttons and controls. + + + +## Installation + +```bash +npx ai-elements@latest add toolbar +``` + +## Features + +- Attaches to any React Flow node +- Bottom positioning by default +- Rounded card design with border +- Theme-aware background styling +- Flexbox layout with gap spacing +- Full TypeScript support +- Compatible with all React Flow NodeToolbar features + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `className` | `string` | - | Additional CSS classes to apply to the toolbar. | +| `...props` | `ComponentProps` | - | Any other props from @xyflow/react NodeToolbar component (position, offset, isVisible, etc.). | diff --git a/.agents/skills/ai-elements/references/transcription.md b/.agents/skills/ai-elements/references/transcription.md new file mode 100644 index 0000000..7dc7ad5 --- /dev/null +++ b/.agents/skills/ai-elements/references/transcription.md @@ -0,0 +1,120 @@ +# Transcription + +A composable component for displaying interactive, synchronized transcripts from AI SDK transcribe() results with click-to-seek functionality. + +The `Transcription` component provides a flexible render props interface for displaying audio transcripts with synchronized playback. It automatically highlights the current segment based on playback time and supports click-to-seek functionality for interactive navigation. + +See `scripts/transcription.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add transcription +``` + +## Features + +- Render props pattern for maximum flexibility +- Automatic segment highlighting based on current time +- Click-to-seek functionality for interactive navigation +- Controlled and uncontrolled component patterns +- Automatic filtering of empty segments +- Visual state indicators (active, past, future) +- Built on Radix UI's `useControllableState` for flexible state management +- Full TypeScript support with AI SDK transcription types + +## Props + +### `` + +Root component that provides context and manages transcript state. Uses render props pattern for rendering segments. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `segments` | `TranscriptionSegment[]` | - | Array of transcription segments from AI SDK transcribe() function. | +| `currentTime` | `number` | `0` | Current playback time in seconds (controlled). | +| `onSeek` | `(time: number) => void` | - | Callback fired when a segment is clicked or when currentTime changes. | +| `children` | `(segment: TranscriptionSegment, index: number) => ReactNode` | - | Render function that receives each segment and its index. | +| `...props` | `Omit` + +Individual segment button with automatic state styling and click-to-seek functionality. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `segment` | `TranscriptionSegment` | - | The transcription segment data. | +| `index` | `number` | - | The segment index. | +| `...props` | `React.ComponentProps<` | - | Any other props are spread to the button element. | + +## Behavior + +### Render Props Pattern + +The component uses a render props pattern where the `children` prop is a function that receives each segment and its index. This provides maximum flexibility for custom rendering while still benefiting from automatic state management and context. + +### Segment Highlighting + +Segments are automatically styled based on their relationship to the current playback time: + +- **Active** (`isActive`): When `currentTime` is within the segment's time range. Styled with primary color. +- **Past** (`isPast`): When `currentTime` is after the segment's end time. Styled with muted foreground. +- **Future**: When `currentTime` is before the segment's start time. Styled with dimmed muted foreground. + +### Click-to-Seek + +When `onSeek` is provided, segments become interactive buttons. Clicking a segment calls `onSeek` with the segment's start time, allowing your audio/video player to seek to that position. + +### Empty Segment Filtering + +The component automatically filters out segments with empty or whitespace-only text to avoid rendering unnecessary elements. + +### State Management + +Uses Radix UI's `useControllableState` hook to support both controlled and uncontrolled patterns. When `currentTime` is provided, the component operates in controlled mode. Otherwise, it maintains its own internal state. + +## Data Format + +The component expects segments from the AI SDK `transcribe()` function: + +```ts +type TranscriptionSegment = { + text: string; + startSecond: number; + endSecond: number; +}; +``` + +## Styling + +The component uses data attributes for custom styling: + +- `data-slot="transcription"`: Root container +- `data-slot="transcription-segment"`: Individual segment button +- `data-active`: Present on the currently playing segment +- `data-index`: The segment's index in the array + +Default segment appearance: + +- Active segment: `text-primary` (primary brand color) +- Past segments: `text-muted-foreground` +- Future segments: `text-muted-foreground/60` (dimmed) +- Interactive segments: `cursor-pointer hover:text-foreground` +- Non-interactive segments: `cursor-default` + +## Accessibility + +- Uses semantic ` + + ); +} +``` + +#### Return Value + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `value` | `string | undefined` | - | The currently selected voice ID. | +| `setValue` | `(value: string | undefined) => void` | - | Function to update the selected voice ID. | +| `open` | `boolean` | - | Whether the dialog is currently open. | +| `setOpen` | `(open: boolean) => void` | - | Function to control the dialog open state. | diff --git a/.agents/skills/ai-elements/references/web-preview.md b/.agents/skills/ai-elements/references/web-preview.md new file mode 100644 index 0000000..685ff24 --- /dev/null +++ b/.agents/skills/ai-elements/references/web-preview.md @@ -0,0 +1,200 @@ +# Web Preview + +A composable component for previewing the result of a generated UI, with support for live examples and code display. + +The `WebPreview` component provides a flexible way to showcase the result of a generated UI component, along with its source code. It is designed for documentation and demo purposes, allowing users to interact with live examples and view the underlying implementation. + +See `scripts/web-preview.tsx` for this example. + +## Installation + +```bash +npx ai-elements@latest add web-preview +``` + +## Usage with AI SDK + +Build a simple v0 clone using the [v0 Platform API](https://v0.dev/docs/api/platform). + +Install the `v0-sdk` package: + +```package-install +npm i v0-sdk +``` + +Add the following component to your frontend: + +```tsx title="app/page.tsx" +"use client"; + +import { + WebPreview, + WebPreviewBody, + WebPreviewNavigation, + WebPreviewUrl, +} from "@/components/ai-elements/web-preview"; +import { useState } from "react"; +import { + PromptInput, + type PromptInputMessage, + PromptInputTextarea, + PromptInputSubmit, +} from "@/components/ai-elements/prompt-input"; +import { Spinner } from "@/components/ui/spinner"; + +const WebPreviewDemo = () => { + const [previewUrl, setPreviewUrl] = useState(""); + const [prompt, setPrompt] = useState(""); + const [isGenerating, setIsGenerating] = useState(false); + + const handleSubmit = async (message: PromptInputMessage) => { + if (!message.text.trim()) return; + setPrompt(""); + + setIsGenerating(true); + try { + const response = await fetch("/api/v0", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ prompt: message.text }), + }); + + const data = await response.json(); + setPreviewUrl(data.demo || "/"); + console.log("Generation finished:", data); + } catch (error) { + console.error("Generation failed:", error); + } finally { + setIsGenerating(false); + } + }; + + return ( +
+
+
+ {isGenerating ? ( +
+ +

+ Generating app, this may take a few seconds... +

+
+ ) : previewUrl ? ( + + + + + + + ) : ( +
+ Your generated app will appear here +
+ )} +
+ + + setPrompt(e.currentTarget.value)} + className="pr-12 min-h-[60px]" + /> + + +
+
+ ); +}; + +export default WebPreviewDemo; +``` + +Add the following route to your backend: + +```ts title="app/api/v0/route.ts" +import { v0 } from "v0-sdk"; + +export async function POST(req: Request) { + const { prompt }: { prompt: string } = await req.json(); + + const result = await v0.chats.create({ + system: "You are an expert coder", + message: prompt, + modelConfiguration: { + modelId: "v0-1.5-sm", + imageGenerations: false, + thinking: false, + }, + }); + + return Response.json({ + demo: result.demo, + webUrl: result.webUrl, + }); +} +``` + +## Features + +- Live preview of UI components +- Composable architecture with dedicated sub-components +- Responsive design modes (Desktop, Tablet, Mobile) +- Navigation controls with back/forward functionality +- URL input and example selector +- Full screen mode support +- Console logging with timestamps +- Context-based state management +- Consistent styling with the design system +- Easy integration into documentation pages + +## Props + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `defaultUrl` | `string` | - | The initial URL to load in the preview. | +| `onUrlChange` | `(url: string) => void` | - | Callback fired when the URL changes. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the navigation container. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `tooltip` | `string` | - | Tooltip text to display on hover. | +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Button component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `...props` | `React.ComponentProps` | - | Any other props are spread to the underlying shadcn/ui Input component. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `loading` | `React.ReactNode` | - | Optional loading indicator to display over the preview. | +| `...props` | `React.IframeHTMLAttributes` | - | Any other props are spread to the underlying iframe. | + +### `` + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `logs` | `Array<{ level: ` | - | Console log entries to display in the console panel. | +| `...props` | `React.HTMLAttributes` | - | Any other props are spread to the root div. | diff --git a/.agents/skills/ai-elements/scripts/agent.tsx b/.agents/skills/ai-elements/scripts/agent.tsx new file mode 100644 index 0000000..e83af14 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/agent.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { + Agent, + AgentContent, + AgentHeader, + AgentInstructions, + AgentOutput, + AgentTool, + AgentTools, +} from "@/components/ai-elements/agent"; +import { z } from "zod"; + +const webSearchTool = { + description: "Search the web for information", + inputSchema: z.object({ + query: z.string().describe("The search query"), + }), +}; + +const readUrlTool = { + description: "Read and parse a URL", + inputSchema: z.object({ + url: z.string().url().describe("The URL to read"), + }), +}; + +const summarizeTool = { + description: "Summarize text into key points", + inputSchema: z.object({ + maxPoints: z.number().optional().describe("Maximum number of key points"), + text: z.string().describe("The text to summarize"), + }), +}; + +const outputSchema = `z.object({ + sentiment: z.enum(['positive', 'negative', 'neutral']), + score: z.number(), + summary: z.string(), +})`; + +const Example = () => ( + + + + + You are a helpful research assistant. Your job is to search the web for + information and summarize findings for the user. Always cite your + sources and provide accurate, up-to-date information. + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/artifact.tsx b/.agents/skills/ai-elements/scripts/artifact.tsx new file mode 100644 index 0000000..eb13063 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/artifact.tsx @@ -0,0 +1,127 @@ +"use client"; + +import { + Artifact, + ArtifactAction, + ArtifactActions, + ArtifactContent, + ArtifactDescription, + ArtifactHeader, + ArtifactTitle, +} from "@/components/ai-elements/artifact"; +import { CodeBlock } from "@/components/ai-elements/code-block"; +import { + CopyIcon, + DownloadIcon, + PlayIcon, + RefreshCwIcon, + ShareIcon, +} from "lucide-react"; + +const handleRun = () => { + console.log("Run"); +}; + +const handleCopy = () => { + console.log("Copy"); +}; + +const handleRegenerate = () => { + console.log("Regenerate"); +}; + +const handleDownload = () => { + console.log("Download"); +}; + +const handleShare = () => { + console.log("Share"); +}; + +const code = `# Dijkstra's Algorithm implementation +import heapq + +def dijkstra(graph, start): + distances = {node: float('inf') for node in graph} + distances[start] = 0 + heap = [(0, start)] + visited = set() + + while heap: + current_distance, current_node = heapq.heappop(heap) + if current_node in visited: + continue + visited.add(current_node) + + for neighbor, weight in graph[current_node].items(): + distance = current_distance + weight + if distance < distances[neighbor]: + distances[neighbor] = distance + heapq.heappush(heap, (distance, neighbor)) + + return distances + +# Example graph + graph = { + 'A': {'B': 1, 'C': 4}, + 'B': {'A': 1, 'C': 2, 'D': 5}, + 'C': {'A': 4, 'B': 2, 'D': 1}, + 'D': {'B': 5, 'C': 1} +} + +print(dijkstra(graph, 'A'))`; + +const Example = () => ( + + +
+ Dijkstra's Algorithm Implementation + Updated 1 minute ago +
+
+ + + + + + + +
+
+ + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/attachments-inline.tsx b/.agents/skills/ai-elements/scripts/attachments-inline.tsx new file mode 100644 index 0000000..ae931a5 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/attachments-inline.tsx @@ -0,0 +1,128 @@ +"use client"; + +import { + Attachment, + AttachmentHoverCard, + AttachmentHoverCardContent, + AttachmentHoverCardTrigger, + AttachmentInfo, + AttachmentPreview, + AttachmentRemove, + Attachments, + getAttachmentLabel, + getMediaCategory, +} from "@/components/ai-elements/attachments"; +import { nanoid } from "nanoid"; +import { memo, useCallback, useState } from "react"; + +const initialAttachments = [ + { + filename: "mountain-landscape.jpg", + id: nanoid(), + mediaType: "image/jpeg", + type: "file" as const, + url: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop", + }, + { + filename: "quarterly-report.pdf", + id: nanoid(), + mediaType: "application/pdf", + type: "file" as const, + url: "", + }, + { + id: nanoid(), + mediaType: "text/html", + title: "React Documentation", + type: "source-document" as const, + url: "https://react.dev", + }, + { + filename: "podcast-episode.mp3", + id: nanoid(), + mediaType: "audio/mp3", + type: "file" as const, + url: "", + }, +]; + +interface AttachmentItemProps { + attachment: (typeof initialAttachments)[0]; + onRemove: (id: string) => void; +} + +const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { + const handleRemove = useCallback( + () => onRemove(attachment.id), + [onRemove, attachment.id] + ); + const mediaCategory = getMediaCategory(attachment); + const label = getAttachmentLabel(attachment); + + return ( + + + +
+
+ +
+ +
+ +
+
+ +
+ {mediaCategory === "image" && + attachment.type === "file" && + attachment.url && ( +
+ {label} +
+ )} +
+

{label}

+ {attachment.mediaType && ( +

+ {attachment.mediaType} +

+ )} +
+
+
+
+ ); +}); + +AttachmentItem.displayName = "AttachmentItem"; + +const Example = () => { + const [attachments, setAttachments] = useState(initialAttachments); + + const handleRemove = useCallback((id: string) => { + setAttachments((prev) => prev.filter((a) => a.id !== id)); + }, []); + + return ( +
+ + {attachments.map((attachment) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/attachments-list.tsx b/.agents/skills/ai-elements/scripts/attachments-list.tsx new file mode 100644 index 0000000..b363dea --- /dev/null +++ b/.agents/skills/ai-elements/scripts/attachments-list.tsx @@ -0,0 +1,95 @@ +"use client"; + +import { + Attachment, + AttachmentInfo, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { nanoid } from "nanoid"; +import { memo, useCallback, useState } from "react"; + +const initialAttachments = [ + { + filename: "mountain-landscape.jpg", + id: nanoid(), + mediaType: "image/jpeg", + type: "file" as const, + url: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop", + }, + { + filename: "quarterly-report-2024.pdf", + id: nanoid(), + mediaType: "application/pdf", + type: "file" as const, + url: "", + }, + { + filename: "product-demo.mp4", + id: nanoid(), + mediaType: "video/mp4", + type: "file" as const, + url: "", + }, + { + filename: "api-reference", + id: nanoid(), + mediaType: "text/html", + title: "API Documentation", + type: "source-document" as const, + url: "https://docs.example.com/api", + }, + { + filename: "meeting-recording.mp3", + id: nanoid(), + mediaType: "audio/mpeg", + type: "file" as const, + url: "", + }, +]; + +interface AttachmentItemProps { + attachment: (typeof initialAttachments)[0]; + onRemove: (id: string) => void; +} + +const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { + const handleRemove = useCallback( + () => onRemove(attachment.id), + [onRemove, attachment.id] + ); + return ( + + + + + + ); +}); + +AttachmentItem.displayName = "AttachmentItem"; + +const Example = () => { + const [attachments, setAttachments] = useState(initialAttachments); + + const handleRemove = useCallback((id: string) => { + setAttachments((prev) => prev.filter((a) => a.id !== id)); + }, []); + + return ( +
+ + {attachments.map((attachment) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/attachments.tsx b/.agents/skills/ai-elements/scripts/attachments.tsx new file mode 100644 index 0000000..fc8e449 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/attachments.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { + Attachment, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { nanoid } from "nanoid"; +import { memo, useCallback, useState } from "react"; + +const initialAttachments = [ + { + filename: "mountain-landscape.jpg", + id: nanoid(), + mediaType: "image/jpeg", + type: "file" as const, + url: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop", + }, + { + filename: "ocean-sunset.jpg", + id: nanoid(), + mediaType: "image/jpeg", + type: "file" as const, + url: "https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?w=400&h=400&fit=crop", + }, + { + filename: "document.pdf", + id: nanoid(), + mediaType: "application/pdf", + type: "file" as const, + url: "", + }, + { + filename: "video.mp4", + id: nanoid(), + mediaType: "video/mp4", + type: "file" as const, + url: "", + }, +]; + +interface AttachmentItemProps { + attachment: (typeof initialAttachments)[0]; + onRemove: (id: string) => void; +} + +const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { + const handleRemove = useCallback( + () => onRemove(attachment.id), + [onRemove, attachment.id] + ); + return ( + + + + + ); +}); + +AttachmentItem.displayName = "AttachmentItem"; + +const Example = () => { + const [attachments, setAttachments] = useState(initialAttachments); + + const handleRemove = useCallback((id: string) => { + setAttachments((prev) => prev.filter((a) => a.id !== id)); + }, []); + + return ( +
+ + {attachments.map((attachment) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/audio-player-remote.tsx b/.agents/skills/ai-elements/scripts/audio-player-remote.tsx new file mode 100644 index 0000000..19d8d89 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/audio-player-remote.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { + AudioPlayer, + AudioPlayerControlBar, + AudioPlayerDurationDisplay, + AudioPlayerElement, + AudioPlayerMuteButton, + AudioPlayerPlayButton, + AudioPlayerSeekBackwardButton, + AudioPlayerSeekForwardButton, + AudioPlayerTimeDisplay, + AudioPlayerTimeRange, + AudioPlayerVolumeRange, +} from "@/components/ai-elements/audio-player"; + +const Example = () => ( +
+ + + + + + + + + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/audio-player.tsx b/.agents/skills/ai-elements/scripts/audio-player.tsx new file mode 100644 index 0000000..bdfd858 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/audio-player.tsx @@ -0,0 +1,68 @@ +"use client"; + +import { + AudioPlayer, + AudioPlayerControlBar, + AudioPlayerDurationDisplay, + AudioPlayerElement, + AudioPlayerMuteButton, + AudioPlayerPlayButton, + AudioPlayerSeekBackwardButton, + AudioPlayerSeekForwardButton, + AudioPlayerTimeDisplay, + AudioPlayerTimeRange, + AudioPlayerVolumeRange, +} from "@/components/ai-elements/audio-player"; +import type { Experimental_SpeechResult as SpeechResult } from "ai"; +import { useEffect, useState } from "react"; + +const Example = () => { + const [data, setData] = useState(null); + + useEffect(() => { + const fetchData = async () => { + const response = await fetch( + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2025-11-10T22_07_46_Hayden_pvc_sp108_s50_sb75_se0_b_m2.mp3" + ); + const arrayBuffer = await response.arrayBuffer(); + const base64 = Buffer.from(arrayBuffer).toString("base64"); + + const newData: SpeechResult["audio"] = { + base64, + format: "mp3", + mediaType: "audio/mpeg", + uint8Array: new Uint8Array(arrayBuffer), + }; + + setData(newData); + }; + + if (!data) { + fetchData(); + } + }, [data]); + + if (!data) { + return
Loading...
; + } + + return ( +
+ + + + + + + + + + + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/chain-of-thought.tsx b/.agents/skills/ai-elements/scripts/chain-of-thought.tsx new file mode 100644 index 0000000..04dac44 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/chain-of-thought.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { + ChainOfThought, + ChainOfThoughtContent, + ChainOfThoughtHeader, + ChainOfThoughtImage, + ChainOfThoughtSearchResult, + ChainOfThoughtSearchResults, + ChainOfThoughtStep, +} from "@/components/ai-elements/chain-of-thought"; +import { Image } from "@/components/ai-elements/image"; +import { ImageIcon, SearchIcon } from "lucide-react"; + +const exampleImage = { + base64: + "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", + mediaType: "image/jpeg", + uint8Array: new Uint8Array([]), +}; + +const ChainOfThoughtExample = () => ( + + + + + + {[ + "https://www.x.com", + "https://www.instagram.com", + "https://www.github.com", + ].map((website) => ( + + {new URL(website).hostname} + + ))} + + + + + + Example generated image + + + + + + + + {["https://www.github.com", "https://www.dribbble.com"].map( + (website) => ( + + {new URL(website).hostname} + + ) + )} + + + + +); + +export default ChainOfThoughtExample; diff --git a/.agents/skills/ai-elements/scripts/checkpoint.tsx b/.agents/skills/ai-elements/scripts/checkpoint.tsx new file mode 100644 index 0000000..426f0a8 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/checkpoint.tsx @@ -0,0 +1,110 @@ +"use client"; + +import { + Checkpoint, + CheckpointIcon, + CheckpointTrigger, +} from "@/components/ai-elements/checkpoint"; +import { Conversation, ConversationContent } from "@/components/ai-elements/conversation"; +import { + Message, + MessageContent, + MessageResponse, +} from "@/components/ai-elements/message"; +import { nanoid } from "nanoid"; +import { Fragment, memo, useCallback, useState } from "react"; + +interface MessageType { + id: string; + role: "user" | "assistant"; + content: string; +} + +const initialMessages: MessageType[] = [ + { + content: "What is React?", + id: nanoid(), + role: "user", + }, + { + content: + "React is a JavaScript library for building user interfaces. It was developed by Facebook and is now maintained by Meta and a community of developers.", + id: nanoid(), + role: "assistant", + }, + { + content: "How does component state work?", + id: nanoid(), + role: "user", + }, +]; + +interface CheckpointItemProps { + checkpoint: { messageCount: number; timestamp: Date }; + onRestore: (messageCount: number) => void; +} + +const CheckpointItem = memo( + ({ checkpoint, onRestore }: CheckpointItemProps) => { + const handleClick = useCallback( + () => onRestore(checkpoint.messageCount), + [onRestore, checkpoint.messageCount] + ); + return ( + + + + Restore checkpoint + + + ); + } +); + +CheckpointItem.displayName = "CheckpointItem"; + +const Example = () => { + const [messages, setMessages] = useState(initialMessages); + const [checkpoints] = useState([ + { messageCount: 2, timestamp: new Date(Date.now() - 3_600_000) }, + ]); + + const handleRestore = useCallback((messageCount: number) => { + setMessages(initialMessages.slice(0, messageCount)); + }, []); + + return ( +
+ + + {messages.map((message, index) => { + const checkpoint = checkpoints.find( + (cp) => cp.messageCount === index + 1 + ); + + return ( + + + + {message.content} + + + {checkpoint && ( + + )} + + ); + })} + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/code-block-dark.tsx b/.agents/skills/ai-elements/scripts/code-block-dark.tsx new file mode 100644 index 0000000..cbafe3f --- /dev/null +++ b/.agents/skills/ai-elements/scripts/code-block-dark.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { + CodeBlock, + CodeBlockActions, + CodeBlockCopyButton, + CodeBlockFilename, + CodeBlockHeader, + CodeBlockTitle, +} from "@/components/ai-elements/code-block"; +import { FileIcon } from "lucide-react"; + +const handleCopy = () => { + console.log("Copied code to clipboard"); +}; + +const handleCopyError = () => { + console.error("Failed to copy code to clipboard"); +}; + +const code = `function MyComponent(props) { + return ( +
+

Hello, {props.name}!

+

This is an example React component.

+
+ ); +}`; + +const Example = () => ( +
+ + + + + MyComponent.jsx + + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/code-block.tsx b/.agents/skills/ai-elements/scripts/code-block.tsx new file mode 100644 index 0000000..6556193 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/code-block.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { + CodeBlock, + CodeBlockActions, + CodeBlockCopyButton, + CodeBlockFilename, + CodeBlockHeader, + CodeBlockLanguageSelector, + CodeBlockLanguageSelectorContent, + CodeBlockLanguageSelectorItem, + CodeBlockLanguageSelectorTrigger, + CodeBlockLanguageSelectorValue, + CodeBlockTitle, +} from "@/components/ai-elements/code-block"; +import { FileIcon } from "lucide-react"; +import { useCallback, useState } from "react"; +import type { BundledLanguage } from "shiki"; + +const codeExamples = { + go: { + code: `package main + +import "fmt" + +func greet(name string) string { + return fmt.Sprintf("Hello, %s!", name) +} + +func main() { + fmt.Println(greet("World")) +}`, + filename: "greet.go", + }, + python: { + code: `def greet(name: str) -> str: + return f"Hello, {name}!" + +print(greet("World"))`, + filename: "greet.py", + }, + rust: { + code: `fn greet(name: &str) -> String { + format!("Hello, {}!", name) +} + +fn main() { + println!("{}", greet("World")); +}`, + filename: "greet.rs", + }, + typescript: { + code: `function greet(name: string): string { + return \`Hello, \${name}!\`; +} + +console.log(greet("World"));`, + filename: "greet.ts", + }, +} as const; + +type Language = keyof typeof codeExamples; + +const languages: { value: Language; label: string }[] = [ + { label: "TypeScript", value: "typescript" }, + { label: "Python", value: "python" }, + { label: "Rust", value: "rust" }, + { label: "Go", value: "go" }, +]; + +const handleCopy = () => { + console.log("Copied code to clipboard"); +}; + +const handleCopyError = () => { + console.error("Failed to copy code to clipboard"); +}; + +const Example = () => { + const [language, setLanguage] = useState("typescript"); + const { code, filename } = codeExamples[language]; + + const handleLanguageChange = useCallback((value: string) => { + setLanguage(value as Language); + }, []); + + return ( + + + + + {filename} + + + + + + + + {languages.map((lang) => ( + + {lang.label} + + ))} + + + + + + + ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/commit.tsx b/.agents/skills/ai-elements/scripts/commit.tsx new file mode 100644 index 0000000..53a4e08 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/commit.tsx @@ -0,0 +1,94 @@ +"use client"; + +import { + Commit, + CommitActions, + CommitAuthor, + CommitAuthorAvatar, + CommitContent, + CommitCopyButton, + CommitFile, + CommitFileAdditions, + CommitFileChanges, + CommitFileDeletions, + CommitFileIcon, + CommitFileInfo, + CommitFilePath, + CommitFileStatus, + CommitFiles, + CommitHash, + CommitHeader, + CommitInfo, + CommitMessage, + CommitMetadata, + CommitSeparator, + CommitTimestamp, +} from "@/components/ai-elements/commit"; + +const handleCopy = () => { + console.log("Copied hash!"); +}; + +const hash = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"; +const timestamp = new Date(Date.now() - 1000 * 60 * 60 * 2); + +const files = [ + { + additions: 150, + deletions: 0, + path: "src/auth/login.tsx", + status: "added" as const, + }, + { + additions: 45, + deletions: 0, + path: "src/auth/logout.tsx", + status: "added" as const, + }, + { + additions: 23, + deletions: 8, + path: "src/lib/session.ts", + status: "modified" as const, + }, +]; + +const Example = () => ( + + + + + + + feat: Add user authentication flow + + {hash.slice(0, 7)} + + + + + + + + + + + {files.map((file) => ( + + + + + {file.path} + + + + + + + ))} + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/confirmation-accepted.tsx b/.agents/skills/ai-elements/scripts/confirmation-accepted.tsx new file mode 100644 index 0000000..5e112d2 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/confirmation-accepted.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { + Confirmation, + ConfirmationAccepted, + ConfirmationRejected, + ConfirmationRequest, + ConfirmationTitle, +} from "@/components/ai-elements/confirmation"; +import { CheckIcon, XIcon } from "lucide-react"; +import { nanoid } from "nanoid"; + +const Example = () => ( +
+ + + + This tool wants to delete the file{" "} + + /tmp/example.txt + + . Do you approve this action? + + + + You approved this tool execution + + + + You rejected this tool execution + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/confirmation-rejected.tsx b/.agents/skills/ai-elements/scripts/confirmation-rejected.tsx new file mode 100644 index 0000000..d29e650 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/confirmation-rejected.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { + Confirmation, + ConfirmationAccepted, + ConfirmationRejected, + ConfirmationRequest, + ConfirmationTitle, +} from "@/components/ai-elements/confirmation"; +import { CheckIcon, XIcon } from "lucide-react"; +import { nanoid } from "nanoid"; + +const Example = () => ( +
+ + + + This tool wants to delete the file{" "} + + /tmp/example.txt + + . Do you approve this action? + + + + You approved this tool execution + + + + You rejected this tool execution + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/confirmation-request.tsx b/.agents/skills/ai-elements/scripts/confirmation-request.tsx new file mode 100644 index 0000000..1fd6854 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/confirmation-request.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { + Confirmation, + ConfirmationAccepted, + ConfirmationAction, + ConfirmationActions, + ConfirmationRejected, + ConfirmationRequest, + ConfirmationTitle, +} from "@/components/ai-elements/confirmation"; +import { CheckIcon, XIcon } from "lucide-react"; +import { nanoid } from "nanoid"; + +const handleReject = () => { + // In production, call respondToConfirmationRequest with approved: false +}; + +const handleApprove = () => { + // In production, call respondToConfirmationRequest with approved: true +}; + +const Example = () => ( +
+ + + + This tool wants to execute a query on the production database: + + SELECT * FROM users WHERE role = 'admin' + + + + + You approved this tool execution + + + + You rejected this tool execution + + + + + Reject + + + Approve + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/confirmation.tsx b/.agents/skills/ai-elements/scripts/confirmation.tsx new file mode 100644 index 0000000..5120b31 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/confirmation.tsx @@ -0,0 +1,55 @@ +"use client"; + +import { + Confirmation, + ConfirmationAccepted, + ConfirmationAction, + ConfirmationActions, + ConfirmationRejected, + ConfirmationRequest, + ConfirmationTitle, +} from "@/components/ai-elements/confirmation"; +import { CheckIcon, XIcon } from "lucide-react"; +import { nanoid } from "nanoid"; + +const handleReject = () => { + // In production, call respondToConfirmationRequest with approved: false +}; + +const handleApprove = () => { + // In production, call respondToConfirmationRequest with approved: true +}; + +const Example = () => ( +
+ + + + This tool wants to delete the file{" "} + + /tmp/example.txt + + . Do you approve this action? + + + + You approved this tool execution + + + + You rejected this tool execution + + + + + Reject + + + Approve + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/context.tsx b/.agents/skills/ai-elements/scripts/context.tsx new file mode 100644 index 0000000..3ed101a --- /dev/null +++ b/.agents/skills/ai-elements/scripts/context.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { + Context, + ContextCacheUsage, + ContextContent, + ContextContentBody, + ContextContentFooter, + ContextContentHeader, + ContextInputUsage, + ContextOutputUsage, + ContextReasoningUsage, + ContextTrigger, +} from "@/components/ai-elements/context"; + +const Example = () => ( +
+ + + + + + + + + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/conversation.tsx b/.agents/skills/ai-elements/scripts/conversation.tsx new file mode 100644 index 0000000..15de5c0 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/conversation.tsx @@ -0,0 +1,177 @@ +"use client"; + +import { + Conversation, + ConversationContent, + ConversationDownload, + ConversationEmptyState, + ConversationScrollButton, +} from "@/components/ai-elements/conversation"; +import { Message, MessageContent } from "@/components/ai-elements/message"; +import { MessageSquareIcon } from "lucide-react"; +import { nanoid } from "nanoid"; +import { useEffect, useState } from "react"; + +const messages: { + key: string; + content: string; + role: "user" | "assistant"; +}[] = [ + { + content: "Hello, how are you?", + key: nanoid(), + role: "user", + }, + { + content: "I'm good, thank you! How can I assist you today?", + key: nanoid(), + role: "assistant", + }, + { + content: "I'm looking for information about your services.", + key: nanoid(), + role: "user", + }, + { + content: + "Sure! We offer a variety of AI solutions. What are you interested in?", + key: nanoid(), + role: "assistant", + }, + { + content: "I'm interested in natural language processing tools.", + key: nanoid(), + role: "user", + }, + { + content: "Great choice! We have several NLP APIs. Would you like a demo?", + key: nanoid(), + role: "assistant", + }, + { + content: "Yes, a demo would be helpful.", + key: nanoid(), + role: "user", + }, + { + content: "Alright, I can show you a sentiment analysis example. Ready?", + key: nanoid(), + role: "assistant", + }, + { + content: "Yes, please proceed.", + key: nanoid(), + role: "user", + }, + { + content: "Here is a sample: 'I love this product!' → Positive sentiment.", + key: nanoid(), + role: "assistant", + }, + { + content: "Impressive! Can it handle multiple languages?", + key: nanoid(), + role: "user", + }, + { + content: "Absolutely, our models support over 20 languages.", + key: nanoid(), + role: "assistant", + }, + { + content: "How do I get started with the API?", + key: nanoid(), + role: "user", + }, + { + content: "You can sign up on our website and get an API key instantly.", + key: nanoid(), + role: "assistant", + }, + { + content: "Is there a free trial available?", + key: nanoid(), + role: "user", + }, + { + content: "Yes, we offer a 14-day free trial with full access.", + key: nanoid(), + role: "assistant", + }, + { + content: "What kind of support do you provide?", + key: nanoid(), + role: "user", + }, + { + content: "We provide 24/7 chat and email support for all users.", + key: nanoid(), + role: "assistant", + }, + { + content: "Thank you for the information!", + key: nanoid(), + role: "user", + }, + { + content: "You're welcome! Let me know if you have any more questions.", + key: nanoid(), + role: "assistant", + }, +]; + +const Example = () => { + const [visibleMessages, setVisibleMessages] = useState< + { + key: string; + content: string; + role: "user" | "assistant"; + }[] + >([]); + + useEffect(() => { + let currentIndex = 0; + const interval = setInterval(() => { + if (currentIndex < messages.length && messages[currentIndex]) { + const currentMessage = messages[currentIndex]; + setVisibleMessages((prev) => [ + ...prev, + { + content: currentMessage.content, + key: currentMessage.key, + role: currentMessage.role, + }, + ]); + currentIndex += 1; + } else { + clearInterval(interval); + } + }, 500); + + return () => clearInterval(interval); + }, []); + + return ( + + + {visibleMessages.length === 0 ? ( + } + title="Start a conversation" + /> + ) : ( + visibleMessages.map(({ key, content, role }) => ( + + {content} + + )) + )} + + + + + ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/environment-variables.tsx b/.agents/skills/ai-elements/scripts/environment-variables.tsx new file mode 100644 index 0000000..703accd --- /dev/null +++ b/.agents/skills/ai-elements/scripts/environment-variables.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { + EnvironmentVariable, + EnvironmentVariableCopyButton, + EnvironmentVariableGroup, + EnvironmentVariableName, + EnvironmentVariableRequired, + EnvironmentVariables, + EnvironmentVariablesContent, + EnvironmentVariablesHeader, + EnvironmentVariablesTitle, + EnvironmentVariablesToggle, + EnvironmentVariableValue, +} from "@/components/ai-elements/environment-variables"; + +const variables = [ + { + name: "DATABASE_URL", + required: true, + value: "postgresql://localhost:5432/mydb", + }, + { name: "API_KEY", required: true, value: "sk-1234567890abcdef" }, + { name: "NODE_ENV", required: false, value: "production" }, + { name: "PORT", required: false, value: "3000" }, +]; + +const handleCopy = () => { + console.log("Copied!"); +}; + +const Example = () => ( + + + + + + + {variables.map((variable) => ( + + + + {variable.required && } + + + + + + + ))} + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/file-tree-basic.tsx b/.agents/skills/ai-elements/scripts/file-tree-basic.tsx new file mode 100644 index 0000000..f9455fd --- /dev/null +++ b/.agents/skills/ai-elements/scripts/file-tree-basic.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { + FileTree, + FileTreeFile, + FileTreeFolder, +} from "@/components/ai-elements/file-tree"; + +const Example = () => ( + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/file-tree-expanded.tsx b/.agents/skills/ai-elements/scripts/file-tree-expanded.tsx new file mode 100644 index 0000000..5312627 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/file-tree-expanded.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { + FileTree, + FileTreeFile, + FileTreeFolder, +} from "@/components/ai-elements/file-tree"; + +const Example = () => ( + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/file-tree-selection.tsx b/.agents/skills/ai-elements/scripts/file-tree-selection.tsx new file mode 100644 index 0000000..6cc489f --- /dev/null +++ b/.agents/skills/ai-elements/scripts/file-tree-selection.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { + FileTree, + FileTreeFile, + FileTreeFolder, +} from "@/components/ai-elements/file-tree"; +import { useState } from "react"; + +const Example = () => { + const [selectedPath, setSelectedPath] = useState(); + + return ( + + + + + + + + ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/file-tree.tsx b/.agents/skills/ai-elements/scripts/file-tree.tsx new file mode 100644 index 0000000..2f26dea --- /dev/null +++ b/.agents/skills/ai-elements/scripts/file-tree.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { + FileTree, + FileTreeFile, + FileTreeFolder, +} from "@/components/ai-elements/file-tree"; +import { useState } from "react"; + +const Example = () => { + const [selectedPath, setSelectedPath] = useState(); + + return ( + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/image.tsx b/.agents/skills/ai-elements/scripts/image.tsx new file mode 100644 index 0000000..6f0448e --- /dev/null +++ b/.agents/skills/ai-elements/scripts/image.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { Image } from "@/components/ai-elements/image"; + +const exampleImage = { + base64: + "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", + mediaType: "image/jpeg", + uint8Array: new Uint8Array([]), +}; + +const Example = () => ( + Example generated image +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/inline-citation.tsx b/.agents/skills/ai-elements/scripts/inline-citation.tsx new file mode 100644 index 0000000..2616d47 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/inline-citation.tsx @@ -0,0 +1,97 @@ +"use client"; + +import { + InlineCitation, + InlineCitationCard, + InlineCitationCardBody, + InlineCitationCardTrigger, + InlineCitationCarousel, + InlineCitationCarouselContent, + InlineCitationCarouselHeader, + InlineCitationCarouselIndex, + InlineCitationCarouselItem, + InlineCitationCarouselNext, + InlineCitationCarouselPrev, + InlineCitationSource, + InlineCitationText, +} from "@/components/ai-elements/inline-citation"; + +const citation = { + sources: [ + { + description: + "A comprehensive study on the recent developments in natural language processing technologies and their applications.", + title: "Advances in Natural Language Processing", + url: "https://example.com/nlp-advances", + }, + { + description: + "An overview of the most significant machine learning breakthroughs in the past year.", + title: "Breakthroughs in Machine Learning", + url: "https://mlnews.org/breakthroughs", + }, + { + description: + "A report on how artificial intelligence is transforming healthcare and diagnostics.", + title: "AI in Healthcare: Current Trends", + url: "https://healthai.com/trends", + }, + { + description: + "A discussion on the ethical considerations and challenges in the development of AI.", + title: "Ethics of Artificial Intelligence", + url: "https://aiethics.org/overview", + }, + { + description: + "Insights into the technical challenges and solutions for scaling deep learning architectures.", + title: "Scaling Deep Learning Models", + url: "https://deeplearninghub.com/scaling-models", + }, + { + description: + "A summary of the latest benchmarks and evaluation metrics for natural language understanding systems.", + title: "Natural Language Understanding Benchmarks", + url: "https://nlubenchmarks.com/latest", + }, + ], + text: "The technology continues to evolve rapidly, with new breakthroughs being announced regularly", +}; + +const Example = () => ( +

+ According to recent studies, artificial intelligence has shown remarkable + progress in natural language processing.{" "} + + {citation.text} + + source.url)} + /> + + + + + + + + + {citation.sources.map((source) => ( + + + + ))} + + + + + + . +

+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/jsx-preview.tsx b/.agents/skills/ai-elements/scripts/jsx-preview.tsx new file mode 100644 index 0000000..d198ed5 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/jsx-preview.tsx @@ -0,0 +1,104 @@ +"use client"; + +import { + JSXPreview, + JSXPreviewContent, + JSXPreviewError, +} from "@/components/ai-elements/jsx-preview"; +import { Button } from "@/components/ui/button"; +import { useCallback, useEffect, useRef, useState } from "react"; + +const handleError = (error: Error) => { + console.log("JSX Parse Error:", error); +}; + +const fullJsx = `
+
+
+ AI +
+
+

AI-Generated Component

+

Rendered from JSX string

+
+
+
+

This component was dynamically rendered from a JSX string. The JSXPreview component supports streaming mode, automatically closing unclosed tags as content arrives.

+
+ React + Streaming + Dynamic +
+
+
+
+ Generated just now + +
+
+
`; + +const Example = () => { + const [streamedJsx, setStreamedJsx] = useState(fullJsx); + const [isStreaming, setIsStreaming] = useState(false); + const intervalRef = useRef | null>(null); + + const simulateStreaming = useCallback(() => { + setIsStreaming(true); + setStreamedJsx(""); + let index = 0; + + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + + intervalRef.current = setInterval(() => { + if (index < fullJsx.length) { + setStreamedJsx(fullJsx.slice(0, index + 15)); + index += 15; + } else { + setIsStreaming(false); + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + } + }, 30); + }, []); + + useEffect( + () => () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + }, + [] + ); + + return ( +
+ + + + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/message.tsx b/.agents/skills/ai-elements/scripts/message.tsx new file mode 100644 index 0000000..892ae75 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/message.tsx @@ -0,0 +1,365 @@ +"use client"; + +import { + Attachment, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { + Message, + MessageAction, + MessageActions, + MessageBranch, + MessageBranchContent, + MessageBranchNext, + MessageBranchPage, + MessageBranchPrevious, + MessageBranchSelector, + MessageContent, + MessageResponse, + MessageToolbar, +} from "@/components/ai-elements/message"; +import { + CopyIcon, + RefreshCcwIcon, + ThumbsDownIcon, + ThumbsUpIcon, +} from "lucide-react"; +import { nanoid } from "nanoid"; +import { memo, useCallback, useState } from "react"; + +const messages: { + key: string; + from: "user" | "assistant"; + versions?: { id: string; content: string }[]; + content?: string; + attachments?: { + id: string; + type: "file"; + url: string; + mediaType?: string; + filename?: string; + }[]; +}[] = [ + { + attachments: [ + { + filename: "palace-of-fine-arts.jpg", + id: nanoid(), + mediaType: "image/jpeg", + type: "file", + url: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop", + }, + { + filename: "react-hooks-guide.pdf", + id: nanoid(), + mediaType: "application/pdf", + type: "file", + url: "", + }, + ], + content: "How do React hooks work and when should I use them?", + from: "user", + key: nanoid(), + }, + { + from: "assistant", + key: nanoid(), + versions: [ + { + content: `# React Hooks Guide + +React hooks are functions that let you "hook into" React state and lifecycle features from function components. Here's what you need to know: + +## Core Hooks + +### useState +Adds state to functional components: + +\`\`\`jsx +const [count, setCount] = useState(0); + +return ( + +); +\`\`\` + +### useEffect +Handles side effects (data fetching, subscriptions, DOM updates): + +\`\`\`jsx +useEffect(() => { + document.title = \`You clicked \${count} times\`; + + // Cleanup function (optional) + return () => { + document.title = 'React App'; + }; +}, [count]); // Dependency array +\`\`\` + +## When to Use Hooks + +- ✅ **Function components** - Hooks only work in function components +- ✅ **Replacing class components** - Modern React favors hooks over classes +- ✅ **Sharing stateful logic** - Create custom hooks to reuse logic +- ❌ **Class components** - Use lifecycle methods instead + +## Rules of Hooks + +1. Only call hooks at the **top level** (not inside loops, conditions, or nested functions) +2. Only call hooks from **React functions** (components or custom hooks) + +Would you like to explore more advanced hooks like \`useCallback\` or \`useMemo\`?`, + id: nanoid(), + }, + { + content: `React hooks are special functions that let you use React features in function components. The most common ones are: + +- **useState** - for managing component state +- **useEffect** - for side effects like data fetching +- **useContext** - for consuming context values +- **useRef** - for accessing DOM elements + +Here's a simple example: + +\`\`\`jsx +function Counter() { + const [count, setCount] = useState(0); + + return ( + + ); +} +\`\`\` + +Which specific hook would you like to learn more about?`, + id: nanoid(), + }, + { + content: `Absolutely! React hooks are a powerful feature introduced in React 16.8. They solve several problems: + +## Key Benefits + +1. **Simpler code** - No need for class components +2. **Reusable logic** - Extract stateful logic into custom hooks +3. **Better organization** - Group related code together + +## Most Popular Hooks + +| Hook | Purpose | +|------|---------| +| useState | Add state to components | +| useEffect | Handle side effects | +| useContext | Access context values | +| useReducer | Complex state logic | +| useCallback | Memoize functions | +| useMemo | Memoize values | + +The beauty of hooks is that they let you reuse stateful logic without changing your component hierarchy. Want to dive into a specific hook?`, + id: nanoid(), + }, + ], + }, +]; + +const handleCopy = (content: string) => { + navigator.clipboard.writeText(content); +}; + +const handleRetry = () => { + console.log("Retrying..."); +}; + +interface LikeActionProps { + messageKey: string; + isLiked: boolean; + onToggle: (key: string) => void; +} + +const LikeAction = memo( + ({ messageKey, isLiked, onToggle }: LikeActionProps) => { + const handleClick = useCallback( + () => onToggle(messageKey), + [messageKey, onToggle] + ); + return ( + + + + ); + } +); + +LikeAction.displayName = "LikeAction"; + +interface DislikeActionProps { + messageKey: string; + isDisliked: boolean; + onToggle: (key: string) => void; +} + +const DislikeAction = memo( + ({ messageKey, isDisliked, onToggle }: DislikeActionProps) => { + const handleClick = useCallback( + () => onToggle(messageKey), + [messageKey, onToggle] + ); + return ( + + + + ); + } +); + +DislikeAction.displayName = "DislikeAction"; + +interface CopyActionProps { + content: string; +} + +const CopyAction = memo(({ content }: CopyActionProps) => { + const handleClick = useCallback(() => handleCopy(content), [content]); + return ( + + + + ); +}); + +CopyAction.displayName = "CopyAction"; + +const Example = () => { + const [liked, setLiked] = useState>({}); + const [disliked, setDisliked] = useState>({}); + + const handleToggleLike = useCallback((key: string) => { + setLiked((prev) => ({ ...prev, [key]: !prev[key] })); + }, []); + + const handleToggleDislike = useCallback((key: string) => { + setDisliked((prev) => ({ ...prev, [key]: !prev[key] })); + }, []); + + return ( +
+ {/* biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Demo component with complex rendering logic */} + {messages.map((message) => ( + + {message.versions?.length && message.versions.length > 1 ? ( + + + {message.versions?.map((version) => ( + + {version.content} + + ))} + + {message.from === "assistant" && ( + + + + + + + + + + + + + v.id)?.content || "" + } + /> + + + )} + + ) : ( +
+ {message.attachments && message.attachments.length > 0 && ( + + {message.attachments.map((attachment) => ( + + + + + ))} + + )} + + {message.from === "assistant" ? ( + {message.content} + ) : ( + message.content + )} + + {message.from === "assistant" && message.versions && ( + + + + + + + + + )} +
+ )} +
+ ))} +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/mic-selector.tsx b/.agents/skills/ai-elements/scripts/mic-selector.tsx new file mode 100644 index 0000000..34ff88d --- /dev/null +++ b/.agents/skills/ai-elements/scripts/mic-selector.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { + MicSelector, + MicSelectorContent, + MicSelectorEmpty, + MicSelectorInput, + MicSelectorItem, + MicSelectorLabel, + MicSelectorList, + MicSelectorTrigger, + MicSelectorValue, +} from "@/components/ai-elements/mic-selector"; + +const handleOpenChange = (open: boolean) => { + console.log("MicSelector is open?", open); +}; + +const handleValueChange = (newValue: string) => { + console.log("MicSelector value:", newValue); +}; + +const Example = () => ( +
+ + + + + + + + + {(devices) => + devices.map((device) => ( + + + + )) + } + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/model-selector.tsx b/.agents/skills/ai-elements/scripts/model-selector.tsx new file mode 100644 index 0000000..5864c15 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/model-selector.tsx @@ -0,0 +1,365 @@ +"use client"; + +import { + ModelSelector, + ModelSelectorContent, + ModelSelectorEmpty, + ModelSelectorGroup, + ModelSelectorInput, + ModelSelectorItem, + ModelSelectorList, + ModelSelectorLogo, + ModelSelectorLogoGroup, + ModelSelectorName, + ModelSelectorTrigger, +} from "@/components/ai-elements/model-selector"; +import { Button } from "@/components/ui/button"; +import { CheckIcon } from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const models = [ + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o", + name: "GPT-4o", + providers: ["openai", "azure"], + }, + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o-mini", + name: "GPT-4o Mini", + providers: ["openai", "azure"], + }, + { + chef: "OpenAI", + chefSlug: "openai", + id: "o1", + name: "o1", + providers: ["openai", "azure"], + }, + { + chef: "OpenAI", + chefSlug: "openai", + id: "o1-mini", + name: "o1 Mini", + providers: ["openai", "azure"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-opus-4-20250514", + name: "Claude 4 Opus", + providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-sonnet-4-20250514", + name: "Claude 4 Sonnet", + providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-3.5-sonnet", + name: "Claude 3.5 Sonnet", + providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-3.5-haiku", + name: "Claude 3.5 Haiku", + providers: ["anthropic", "azure", "google-vertex", "amazon-bedrock"], + }, + { + chef: "Google", + chefSlug: "google", + id: "gemini-2.0-flash-exp", + name: "Gemini 2.0 Flash", + providers: ["google", "google-vertex"], + }, + { + chef: "Google", + chefSlug: "google", + id: "gemini-1.5-pro", + name: "Gemini 1.5 Pro", + providers: ["google", "google-vertex"], + }, + { + chef: "Google", + chefSlug: "google", + id: "gemini-1.5-flash", + name: "Gemini 1.5 Flash", + providers: ["google", "google-vertex"], + }, + { + chef: "Meta", + chefSlug: "llama", + id: "llama-3.3-70b", + name: "Llama 3.3 70B", + providers: ["groq", "togetherai", "amazon-bedrock"], + }, + { + chef: "Meta", + chefSlug: "llama", + id: "llama-3.1-405b", + name: "Llama 3.1 405B", + providers: ["togetherai", "amazon-bedrock"], + }, + { + chef: "Meta", + chefSlug: "llama", + id: "llama-3.1-70b", + name: "Llama 3.1 70B", + providers: ["groq", "togetherai", "amazon-bedrock"], + }, + { + chef: "Meta", + chefSlug: "llama", + id: "llama-3.1-8b", + name: "Llama 3.1 8B", + providers: ["groq", "togetherai"], + }, + { + chef: "DeepSeek", + chefSlug: "deepseek", + id: "deepseek-r1", + name: "DeepSeek R1", + providers: ["deepseek", "openrouter"], + }, + { + chef: "DeepSeek", + chefSlug: "deepseek", + id: "deepseek-v3", + name: "DeepSeek V3", + providers: ["deepseek", "openrouter"], + }, + { + chef: "DeepSeek", + chefSlug: "deepseek", + id: "deepseek-coder-v2", + name: "DeepSeek Coder V2", + providers: ["deepseek", "openrouter"], + }, + { + chef: "Mistral AI", + chefSlug: "mistral", + id: "mistral-large", + name: "Mistral Large", + providers: ["mistral", "azure"], + }, + { + chef: "Mistral AI", + chefSlug: "mistral", + id: "mistral-small", + name: "Mistral Small", + providers: ["mistral", "azure"], + }, + { + chef: "Mistral AI", + chefSlug: "mistral", + id: "codestral", + name: "Codestral", + providers: ["mistral"], + }, + { + chef: "Alibaba", + chefSlug: "alibaba", + id: "qwen-2.5-72b", + name: "Qwen 2.5 72B", + providers: ["alibaba", "openrouter"], + }, + { + chef: "Alibaba", + chefSlug: "alibaba", + id: "qwen-2.5-coder-32b", + name: "Qwen 2.5 Coder 32B", + providers: ["alibaba", "openrouter"], + }, + { + chef: "Alibaba", + chefSlug: "alibaba", + id: "qwen-max", + name: "Qwen Max", + providers: ["alibaba"], + }, + { + chef: "Cohere", + chefSlug: "cohere", + id: "command-r-plus", + name: "Command R+", + providers: ["cohere", "azure", "amazon-bedrock"], + }, + { + chef: "Cohere", + chefSlug: "cohere", + id: "command-r", + name: "Command R", + providers: ["cohere", "azure", "amazon-bedrock"], + }, + { + chef: "xAI", + chefSlug: "xai", + id: "grok-3", + name: "Grok 3", + providers: ["xai"], + }, + { + chef: "xAI", + chefSlug: "xai", + id: "grok-2-1212", + name: "Grok 2 1212", + providers: ["xai"], + }, + { + chef: "xAI", + chefSlug: "xai", + id: "grok-vision", + name: "Grok Vision", + providers: ["xai"], + }, + { + chef: "Moonshot AI", + chefSlug: "moonshotai", + id: "moonshot-v1-128k", + name: "Moonshot v1 128K", + providers: ["moonshotai"], + }, + { + chef: "Moonshot AI", + chefSlug: "moonshotai", + id: "moonshot-v1-32k", + name: "Moonshot v1 32K", + providers: ["moonshotai"], + }, + { + chef: "Perplexity", + chefSlug: "perplexity", + id: "sonar-pro", + name: "Sonar Pro", + providers: ["perplexity"], + }, + { + chef: "Perplexity", + chefSlug: "perplexity", + id: "sonar", + name: "Sonar", + providers: ["perplexity"], + }, + { + chef: "Vercel", + chefSlug: "v0", + id: "v0-chat", + name: "v0 Chat", + providers: ["vercel"], + }, + { + chef: "Amazon", + chefSlug: "amazon-bedrock", + id: "nova-pro", + name: "Nova Pro", + providers: ["amazon-bedrock"], + }, + { + chef: "Amazon", + chefSlug: "amazon-bedrock", + id: "nova-lite", + name: "Nova Lite", + providers: ["amazon-bedrock"], + }, + { + chef: "Amazon", + chefSlug: "amazon-bedrock", + id: "nova-micro", + name: "Nova Micro", + providers: ["amazon-bedrock"], + }, +]; + +interface ModelItemProps { + model: (typeof models)[0]; + selectedModel: string; + onSelect: (id: string) => void; +} + +const ModelItem = memo(({ model, selectedModel, onSelect }: ModelItemProps) => { + const handleSelect = useCallback( + () => onSelect(model.id), + [onSelect, model.id] + ); + return ( + + + {model.name} + + {model.providers.map((provider) => ( + + ))} + + {selectedModel === model.id ? ( + + ) : ( +
+ )} + + ); +}); + +ModelItem.displayName = "ModelItem"; + +const Example = () => { + const [open, setOpen] = useState(false); + const [selectedModel, setSelectedModel] = useState("gpt-4o"); + + const handleModelSelect = useCallback((id: string) => { + setSelectedModel(id); + setOpen(false); + }, []); + + const selectedModelData = models.find((model) => model.id === selectedModel); + + // Get unique chefs in order of appearance + const chefs = [...new Set(models.map((model) => model.chef))]; + + return ( +
+ + + + + + + + No models found. + {chefs.map((chef) => ( + + {models + .filter((model) => model.chef === chef) + .map((model) => ( + + ))} + + ))} + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/open-in-chat.tsx b/.agents/skills/ai-elements/scripts/open-in-chat.tsx new file mode 100644 index 0000000..d47f034 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/open-in-chat.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { + OpenIn, + OpenInChatGPT, + OpenInClaude, + OpenInContent, + OpenInCursor, + OpenInScira, + OpenInT3, + OpenInTrigger, + OpenInv0, +} from "@/components/ai-elements/open-in-chat"; + +const Example = () => { + const sampleQuery = "How can I implement authentication in Next.js?"; + + return ( + + + + + + + + + + + + ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/package-info.tsx b/.agents/skills/ai-elements/scripts/package-info.tsx new file mode 100644 index 0000000..cdb7c18 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/package-info.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { + PackageInfo, + PackageInfoChangeType, + PackageInfoContent, + PackageInfoDependencies, + PackageInfoDependency, + PackageInfoDescription, + PackageInfoHeader, + PackageInfoName, + PackageInfoVersion, +} from "@/components/ai-elements/package-info"; + +const Example = () => ( +
+ + + + + + + + A JavaScript library for building user interfaces. + + + + + + + + + + + + + + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/persona-command.tsx b/.agents/skills/ai-elements/scripts/persona-command.tsx new file mode 100644 index 0000000..43897cf --- /dev/null +++ b/.agents/skills/ai-elements/scripts/persona-command.tsx @@ -0,0 +1,110 @@ +"use client"; + +import type { PersonaState } from "@/components/ai-elements/persona"; +import { Persona } from "@/components/ai-elements/persona"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { LucideIcon } from "lucide-react"; +import { + BrainIcon, + CircleIcon, + EyeClosedIcon, + MegaphoneIcon, + MicIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const states: { + state: PersonaState; + icon: LucideIcon; + label: string; +}[] = [ + { + icon: CircleIcon, + label: "Idle", + state: "idle", + }, + { + icon: MicIcon, + label: "Listening", + state: "listening", + }, + { + icon: BrainIcon, + label: "Thinking", + state: "thinking", + }, + { + icon: MegaphoneIcon, + label: "Speaking", + state: "speaking", + }, + { + icon: EyeClosedIcon, + label: "Asleep", + state: "asleep", + }, +]; + +interface StateButtonProps { + state: (typeof states)[0]; + currentState: PersonaState; + onStateChange: (state: PersonaState) => void; +} + +const StateButton = memo( + ({ state, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback( + () => onStateChange(state.state), + [onStateChange, state.state] + ); + return ( + + + + + {state.label} + + ); + } +); + +StateButton.displayName = "StateButton"; + +const Example = () => { + const [currentState, setCurrentState] = useState("idle"); + + const handleStateChange = useCallback((state: PersonaState) => { + setCurrentState(state); + }, []); + + return ( +
+ + + + {states.map((state) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/persona-glint.tsx b/.agents/skills/ai-elements/scripts/persona-glint.tsx new file mode 100644 index 0000000..f7ccf90 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/persona-glint.tsx @@ -0,0 +1,110 @@ +"use client"; + +import type { PersonaState } from "@/components/ai-elements/persona"; +import { Persona } from "@/components/ai-elements/persona"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { LucideIcon } from "lucide-react"; +import { + BrainIcon, + CircleIcon, + EyeClosedIcon, + MegaphoneIcon, + MicIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const states: { + state: PersonaState; + icon: LucideIcon; + label: string; +}[] = [ + { + icon: CircleIcon, + label: "Idle", + state: "idle", + }, + { + icon: MicIcon, + label: "Listening", + state: "listening", + }, + { + icon: BrainIcon, + label: "Thinking", + state: "thinking", + }, + { + icon: MegaphoneIcon, + label: "Speaking", + state: "speaking", + }, + { + icon: EyeClosedIcon, + label: "Asleep", + state: "asleep", + }, +]; + +interface StateButtonProps { + state: (typeof states)[0]; + currentState: PersonaState; + onStateChange: (state: PersonaState) => void; +} + +const StateButton = memo( + ({ state, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback( + () => onStateChange(state.state), + [onStateChange, state.state] + ); + return ( + + + + + {state.label} + + ); + } +); + +StateButton.displayName = "StateButton"; + +const Example = () => { + const [currentState, setCurrentState] = useState("idle"); + + const handleStateChange = useCallback((state: PersonaState) => { + setCurrentState(state); + }, []); + + return ( +
+ + + + {states.map((state) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/persona-halo.tsx b/.agents/skills/ai-elements/scripts/persona-halo.tsx new file mode 100644 index 0000000..b810677 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/persona-halo.tsx @@ -0,0 +1,110 @@ +"use client"; + +import type { PersonaState } from "@/components/ai-elements/persona"; +import { Persona } from "@/components/ai-elements/persona"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { LucideIcon } from "lucide-react"; +import { + BrainIcon, + CircleIcon, + EyeClosedIcon, + MegaphoneIcon, + MicIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const states: { + state: PersonaState; + icon: LucideIcon; + label: string; +}[] = [ + { + icon: CircleIcon, + label: "Idle", + state: "idle", + }, + { + icon: MicIcon, + label: "Listening", + state: "listening", + }, + { + icon: BrainIcon, + label: "Thinking", + state: "thinking", + }, + { + icon: MegaphoneIcon, + label: "Speaking", + state: "speaking", + }, + { + icon: EyeClosedIcon, + label: "Asleep", + state: "asleep", + }, +]; + +interface StateButtonProps { + state: (typeof states)[0]; + currentState: PersonaState; + onStateChange: (state: PersonaState) => void; +} + +const StateButton = memo( + ({ state, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback( + () => onStateChange(state.state), + [onStateChange, state.state] + ); + return ( + + + + + {state.label} + + ); + } +); + +StateButton.displayName = "StateButton"; + +const Example = () => { + const [currentState, setCurrentState] = useState("idle"); + + const handleStateChange = useCallback((state: PersonaState) => { + setCurrentState(state); + }, []); + + return ( +
+ + + + {states.map((state) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/persona-mana.tsx b/.agents/skills/ai-elements/scripts/persona-mana.tsx new file mode 100644 index 0000000..f762dd0 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/persona-mana.tsx @@ -0,0 +1,110 @@ +"use client"; + +import type { PersonaState } from "@/components/ai-elements/persona"; +import { Persona } from "@/components/ai-elements/persona"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { LucideIcon } from "lucide-react"; +import { + BrainIcon, + CircleIcon, + EyeClosedIcon, + MegaphoneIcon, + MicIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const states: { + state: PersonaState; + icon: LucideIcon; + label: string; +}[] = [ + { + icon: CircleIcon, + label: "Idle", + state: "idle", + }, + { + icon: MicIcon, + label: "Listening", + state: "listening", + }, + { + icon: BrainIcon, + label: "Thinking", + state: "thinking", + }, + { + icon: MegaphoneIcon, + label: "Speaking", + state: "speaking", + }, + { + icon: EyeClosedIcon, + label: "Asleep", + state: "asleep", + }, +]; + +interface StateButtonProps { + state: (typeof states)[0]; + currentState: PersonaState; + onStateChange: (state: PersonaState) => void; +} + +const StateButton = memo( + ({ state, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback( + () => onStateChange(state.state), + [onStateChange, state.state] + ); + return ( + + + + + {state.label} + + ); + } +); + +StateButton.displayName = "StateButton"; + +const Example = () => { + const [currentState, setCurrentState] = useState("idle"); + + const handleStateChange = useCallback((state: PersonaState) => { + setCurrentState(state); + }, []); + + return ( +
+ + + + {states.map((state) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/persona-obsidian.tsx b/.agents/skills/ai-elements/scripts/persona-obsidian.tsx new file mode 100644 index 0000000..cb6103d --- /dev/null +++ b/.agents/skills/ai-elements/scripts/persona-obsidian.tsx @@ -0,0 +1,110 @@ +"use client"; + +import type { PersonaState } from "@/components/ai-elements/persona"; +import { Persona } from "@/components/ai-elements/persona"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { LucideIcon } from "lucide-react"; +import { + BrainIcon, + CircleIcon, + EyeClosedIcon, + MegaphoneIcon, + MicIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const states: { + state: PersonaState; + icon: LucideIcon; + label: string; +}[] = [ + { + icon: CircleIcon, + label: "Idle", + state: "idle", + }, + { + icon: MicIcon, + label: "Listening", + state: "listening", + }, + { + icon: BrainIcon, + label: "Thinking", + state: "thinking", + }, + { + icon: MegaphoneIcon, + label: "Speaking", + state: "speaking", + }, + { + icon: EyeClosedIcon, + label: "Asleep", + state: "asleep", + }, +]; + +interface StateButtonProps { + state: (typeof states)[0]; + currentState: PersonaState; + onStateChange: (state: PersonaState) => void; +} + +const StateButton = memo( + ({ state, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback( + () => onStateChange(state.state), + [onStateChange, state.state] + ); + return ( + + + + + {state.label} + + ); + } +); + +StateButton.displayName = "StateButton"; + +const Example = () => { + const [currentState, setCurrentState] = useState("idle"); + + const handleStateChange = useCallback((state: PersonaState) => { + setCurrentState(state); + }, []); + + return ( +
+ + + + {states.map((state) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/persona-opal.tsx b/.agents/skills/ai-elements/scripts/persona-opal.tsx new file mode 100644 index 0000000..dae6c86 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/persona-opal.tsx @@ -0,0 +1,110 @@ +"use client"; + +import type { PersonaState } from "@/components/ai-elements/persona"; +import { Persona } from "@/components/ai-elements/persona"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { LucideIcon } from "lucide-react"; +import { + BrainIcon, + CircleIcon, + EyeClosedIcon, + MegaphoneIcon, + MicIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +interface StateButtonProps { + state: { state: PersonaState; icon: LucideIcon; label: string }; + currentState: PersonaState; + onStateChange: (state: PersonaState) => void; +} + +const StateButton = memo( + ({ state, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback( + () => onStateChange(state.state), + [onStateChange, state.state] + ); + return ( + + + + + {state.label} + + ); + } +); + +StateButton.displayName = "StateButton"; + +const states: { + state: PersonaState; + icon: LucideIcon; + label: string; +}[] = [ + { + icon: CircleIcon, + label: "Idle", + state: "idle", + }, + { + icon: MicIcon, + label: "Listening", + state: "listening", + }, + { + icon: BrainIcon, + label: "Thinking", + state: "thinking", + }, + { + icon: MegaphoneIcon, + label: "Speaking", + state: "speaking", + }, + { + icon: EyeClosedIcon, + label: "Asleep", + state: "asleep", + }, +]; + +const Example = () => { + const [currentState, setCurrentState] = useState("idle"); + + const handleStateChange = useCallback((state: PersonaState) => { + setCurrentState(state); + }, []); + + return ( +
+ + + + {states.map((state) => ( + + ))} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/plan.tsx b/.agents/skills/ai-elements/scripts/plan.tsx new file mode 100644 index 0000000..a0091d8 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/plan.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { + Plan, + PlanAction, + PlanContent, + PlanDescription, + PlanFooter, + PlanHeader, + PlanTitle, + PlanTrigger, +} from "@/components/ai-elements/plan"; +import { Button } from "@/components/ui/button"; +import { FileText } from "lucide-react"; + +const Example = () => ( + + +
+
+ + Rewrite AI Elements to SolidJS +
+ + Rewrite the AI Elements component library from React to SolidJS while + maintaining compatibility with existing React-based shadcn/ui + components using solid-js/compat, updating all 29 components and their + test suite. + +
+ +
+ +
+
+

Overview

+

+ This plan outlines the migration strategy for converting the AI + Elements library from React to SolidJS, ensuring compatibility and + maintaining existing functionality. +

+
+
+

Key Steps

+
    +
  • Set up SolidJS project structure
  • +
  • Install solid-js/compat for React compatibility
  • +
  • Migrate components one by one
  • +
  • Update test suite for each component
  • +
  • Verify compatibility with shadcn/ui
  • +
+
+
+
+ + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/prompt-input-cursor.tsx b/.agents/skills/ai-elements/scripts/prompt-input-cursor.tsx new file mode 100644 index 0000000..da79d2f --- /dev/null +++ b/.agents/skills/ai-elements/scripts/prompt-input-cursor.tsx @@ -0,0 +1,502 @@ +"use client"; + +import type { AttachmentData } from "@/components/ai-elements/attachments"; +import { + Attachment, + AttachmentInfo, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { + ModelSelector, + ModelSelectorContent, + ModelSelectorEmpty, + ModelSelectorGroup, + ModelSelectorInput, + ModelSelectorItem, + ModelSelectorList, + ModelSelectorLogo, + ModelSelectorLogoGroup, + ModelSelectorName, + ModelSelectorTrigger, +} from "@/components/ai-elements/model-selector"; +import type { PromptInputMessage } from "@/components/ai-elements/prompt-input"; +import { + PromptInput, + PromptInputBody, + PromptInputButton, + PromptInputCommand, + PromptInputCommandEmpty, + PromptInputCommandGroup, + PromptInputCommandInput, + PromptInputCommandItem, + PromptInputCommandList, + PromptInputCommandSeparator, + PromptInputFooter, + PromptInputHeader, + PromptInputHoverCard, + PromptInputHoverCardContent, + PromptInputHoverCardTrigger, + PromptInputProvider, + PromptInputSubmit, + PromptInputTab, + PromptInputTabBody, + PromptInputTabItem, + PromptInputTabLabel, + PromptInputTextarea, + PromptInputTools, + usePromptInputAttachments, + usePromptInputReferencedSources, +} from "@/components/ai-elements/prompt-input"; +import { Button } from "@/components/ui/button"; +import type { SourceDocumentUIPart } from "ai"; +import { + AtSignIcon, + CheckIcon, + FilesIcon, + GlobeIcon, + ImageIcon, + RulerIcon, +} from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const models = [ + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o", + name: "GPT-4o", + providers: ["openai", "azure"], + }, + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o-mini", + name: "GPT-4o Mini", + providers: ["openai", "azure"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-opus-4-20250514", + name: "Claude 4 Opus", + providers: ["anthropic", "azure", "google", "amazon-bedrock"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-sonnet-4-20250514", + name: "Claude 4 Sonnet", + providers: ["anthropic", "azure", "google", "amazon-bedrock"], + }, + { + chef: "Google", + chefSlug: "google", + id: "gemini-2.0-flash-exp", + name: "Gemini 2.0 Flash", + providers: ["google"], + }, +]; + +const SUBMITTING_TIMEOUT = 200; +const STREAMING_TIMEOUT = 2000; + +interface AttachmentItemProps { + attachment: AttachmentData; + onRemove: (id: string) => void; +} + +const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { + const handleRemove = useCallback( + () => onRemove(attachment.id), + [onRemove, attachment.id] + ); + return ( + + + + + ); +}); + +AttachmentItem.displayName = "AttachmentItem"; + +interface SourceItemProps { + source: AttachmentData; + onRemove: (id: string) => void; +} + +const SourceItem = memo(({ source, onRemove }: SourceItemProps) => { + const handleRemove = useCallback( + () => onRemove(source.id), + [onRemove, source.id] + ); + return ( + + + + + + ); +}); + +SourceItem.displayName = "SourceItem"; + +interface ModelItemProps { + m: (typeof models)[0]; + selectedModel: string; + onSelect: (id: string) => void; +} + +const ModelItem = memo(({ m, selectedModel, onSelect }: ModelItemProps) => { + const handleSelect = useCallback(() => onSelect(m.id), [onSelect, m.id]); + return ( + + + {m.name} + + {m.providers.map((provider) => ( + + ))} + + {selectedModel === m.id ? ( + + ) : ( +
+ )} + + ); +}); + +ModelItem.displayName = "ModelItem"; + +interface SourceCommandItemProps { + source: SourceDocumentUIPart; + onAdd: (source: SourceDocumentUIPart) => void; +} + +const SourceCommandItem = memo(({ source, onAdd }: SourceCommandItemProps) => { + const handleSelect = useCallback(() => onAdd(source), [onAdd, source]); + return ( + + +
+ {source.title} + {source.filename} +
+
+ ); +}); + +SourceCommandItem.displayName = "SourceCommandItem"; + +const sampleSources: SourceDocumentUIPart[] = [ + { + filename: "packages/elements/src", + mediaType: "text/plain", + sourceId: "1", + title: "prompt-input.tsx", + type: "source-document", + }, + { + filename: "apps/test/app/examples", + mediaType: "text/plain", + sourceId: "2", + title: "queue.tsx", + type: "source-document", + }, + { + filename: "packages/elements/src", + mediaType: "text/plain", + sourceId: "3", + title: "queue.tsx", + type: "source-document", + }, +]; + +const sampleTabs = { + active: [{ path: "packages/elements/src/task-queue-panel.tsx" }], + recents: [ + { path: "apps/test/app/examples/task-queue-panel.tsx" }, + { path: "apps/test/app/page.tsx" }, + { path: "packages/elements/src/task.tsx" }, + { path: "apps/test/app/examples/prompt-input.tsx" }, + { path: "packages/elements/src/queue.tsx" }, + { path: "apps/test/app/examples/queue.tsx" }, + ], +}; + +const PromptInputAttachmentsDisplay = () => { + const attachments = usePromptInputAttachments(); + + const handleRemove = useCallback( + (id: string) => attachments.remove(id), + [attachments] + ); + + if (attachments.files.length === 0) { + return null; + } + + return ( + + {attachments.files.map((attachment) => ( + + ))} + + ); +}; + +const PromptInputReferencedSourcesDisplay = () => { + const refs = usePromptInputReferencedSources(); + + const handleRemove = useCallback((id: string) => refs.remove(id), [refs]); + + if (refs.sources.length === 0) { + return null; + } + + return ( + + {refs.sources.map((source) => ( + + ))} + + ); +}; + +const SampleFilesMenu = () => { + const refs = usePromptInputReferencedSources(); + + const handleAdd = useCallback( + (source: SourceDocumentUIPart) => refs.add(source), + [refs] + ); + + return ( + + + + + No results found. + + + + + Active Tabs + + + + + + {sampleSources + .filter( + (source) => + !refs.sources.some( + (s) => + s.title === source.title && s.filename === source.filename + ) + ) + .map((source) => ( + + ))} + + + + ); +}; + +const Example = () => { + const [model, setModel] = useState(models[0].id); + const [modelSelectorOpen, setModelSelectorOpen] = useState(false); + const [status, setStatus] = useState< + "submitted" | "streaming" | "ready" | "error" + >("ready"); + + const selectedModelData = models.find((m) => m.id === model); + + const handleModelSelect = useCallback((id: string) => { + setModel(id); + setModelSelectorOpen(false); + }, []); + + const handleSubmit = useCallback((message: PromptInputMessage) => { + const hasText = Boolean(message.text); + const hasAttachments = Boolean(message.files?.length); + + if (!(hasText || hasAttachments)) { + return; + } + + setStatus("submitted"); + + setTimeout(() => { + setStatus("streaming"); + }, SUBMITTING_TIMEOUT); + + setTimeout(() => { + setStatus("ready"); + }, STREAMING_TIMEOUT); + }, []); + + return ( +
+ + + + + + + + + + + + + + + + + + 1 + + + +
+

+ Attached Project Rules +

+

+ Always Apply: +

+

ultracite.mdc

+
+

+ Click to manage +

+
+
+ + + + + 1 Tab + + + + + Active Tabs + + {sampleTabs.active.map((tab) => ( + + + + {tab.path} + + + ))} + + + + Recents + + {sampleTabs.recents.map((tab) => ( + + + + {tab.path} + + + ))} + + +
+ Only file paths are included +
+
+
+ + +
+ + + + + + + + + {selectedModelData?.chefSlug && ( + + )} + {selectedModelData?.name && ( + + {selectedModelData.name} + + )} + + + + + + No models found. + {["OpenAI", "Anthropic", "Google"].map((chef) => ( + + {models + .filter((m) => m.chef === chef) + .map((m) => ( + + ))} + + ))} + + + + +
+ + +
+
+
+
+
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/prompt-input-tooltip.tsx b/.agents/skills/ai-elements/scripts/prompt-input-tooltip.tsx new file mode 100644 index 0000000..c57e5ee --- /dev/null +++ b/.agents/skills/ai-elements/scripts/prompt-input-tooltip.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { + PromptInput, + PromptInputBody, + PromptInputButton, + PromptInputFooter, + PromptInputSubmit, + PromptInputTextarea, + PromptInputTools, +} from "@/components/ai-elements/prompt-input"; +import { GlobeIcon, MicIcon, PaperclipIcon } from "lucide-react"; + +const handleSubmit = () => { + // Handle submit +}; + +const Example = () => ( + + + + + + + + + + + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/prompt-input.tsx b/.agents/skills/ai-elements/scripts/prompt-input.tsx new file mode 100644 index 0000000..bfe082d --- /dev/null +++ b/.agents/skills/ai-elements/scripts/prompt-input.tsx @@ -0,0 +1,267 @@ +"use client"; + +import { + Attachment, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { + ModelSelector, + ModelSelectorContent, + ModelSelectorEmpty, + ModelSelectorGroup, + ModelSelectorInput, + ModelSelectorItem, + ModelSelectorList, + ModelSelectorLogo, + ModelSelectorLogoGroup, + ModelSelectorName, + ModelSelectorTrigger, +} from "@/components/ai-elements/model-selector"; +import type { PromptInputMessage } from "@/components/ai-elements/prompt-input"; +import { + PromptInput, + PromptInputActionAddAttachments, + PromptInputActionAddScreenshot, + PromptInputActionMenu, + PromptInputActionMenuContent, + PromptInputActionMenuTrigger, + PromptInputBody, + PromptInputButton, + PromptInputFooter, + PromptInputProvider, + PromptInputSubmit, + PromptInputTextarea, + PromptInputTools, + usePromptInputAttachments, +} from "@/components/ai-elements/prompt-input"; +import { CheckIcon, GlobeIcon } from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const models = [ + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o", + name: "GPT-4o", + providers: ["openai", "azure"], + }, + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o-mini", + name: "GPT-4o Mini", + providers: ["openai", "azure"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-opus-4-20250514", + name: "Claude 4 Opus", + providers: ["anthropic", "azure", "google", "amazon-bedrock"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-sonnet-4-20250514", + name: "Claude 4 Sonnet", + providers: ["anthropic", "azure", "google", "amazon-bedrock"], + }, + { + chef: "Google", + chefSlug: "google", + id: "gemini-2.0-flash-exp", + name: "Gemini 2.0 Flash", + providers: ["google"], + }, +]; + +const SUBMITTING_TIMEOUT = 200; +const STREAMING_TIMEOUT = 2000; + +interface AttachmentItemProps { + attachment: { + id: string; + type: "file"; + filename?: string; + mediaType?: string; + url: string; + }; + onRemove: (id: string) => void; +} + +const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { + const handleRemove = useCallback( + () => onRemove(attachment.id), + [onRemove, attachment.id] + ); + return ( + + + + + ); +}); + +AttachmentItem.displayName = "AttachmentItem"; + +interface ModelItemProps { + m: (typeof models)[0]; + selectedModel: string; + onSelect: (id: string) => void; +} + +const ModelItem = memo(({ m, selectedModel, onSelect }: ModelItemProps) => { + const handleSelect = useCallback(() => onSelect(m.id), [onSelect, m.id]); + return ( + + + {m.name} + + {m.providers.map((provider) => ( + + ))} + + {selectedModel === m.id ? ( + + ) : ( +
+ )} + + ); +}); + +ModelItem.displayName = "ModelItem"; + +const PromptInputAttachmentsDisplay = () => { + const attachments = usePromptInputAttachments(); + + const handleRemove = useCallback( + (id: string) => attachments.remove(id), + [attachments] + ); + + if (attachments.files.length === 0) { + return null; + } + + return ( + + {attachments.files.map((attachment) => ( + + ))} + + ); +}; + +const Example = () => { + const [model, setModel] = useState(models[0].id); + const [modelSelectorOpen, setModelSelectorOpen] = useState(false); + const [status, setStatus] = useState< + "submitted" | "streaming" | "ready" | "error" + >("ready"); + + const selectedModelData = models.find((m) => m.id === model); + + const handleModelSelect = useCallback((id: string) => { + setModel(id); + setModelSelectorOpen(false); + }, []); + + const handleSubmit = useCallback((message: PromptInputMessage) => { + const hasText = Boolean(message.text); + const hasAttachments = Boolean(message.files?.length); + + if (!(hasText || hasAttachments)) { + return; + } + + setStatus("submitted"); + + // eslint-disable-next-line no-console + console.log("Submitting message:", message); + + setTimeout(() => { + setStatus("streaming"); + }, SUBMITTING_TIMEOUT); + + setTimeout(() => { + setStatus("ready"); + }, STREAMING_TIMEOUT); + }, []); + + return ( +
+ + + + + + + + + + + + + + + + + + Search + + + + + {selectedModelData?.chefSlug && ( + + )} + {selectedModelData?.name && ( + + {selectedModelData.name} + + )} + + + + + + No models found. + {["OpenAI", "Anthropic", "Google"].map((chef) => ( + + {models + .filter((m) => m.chef === chef) + .map((m) => ( + + ))} + + ))} + + + + + + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/queue-prompt-input.tsx b/.agents/skills/ai-elements/scripts/queue-prompt-input.tsx new file mode 100644 index 0000000..255f8e3 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/queue-prompt-input.tsx @@ -0,0 +1,394 @@ +"use client"; + +import { + Attachment, + AttachmentPreview, + AttachmentRemove, + Attachments, +} from "@/components/ai-elements/attachments"; +import { + ModelSelector, + ModelSelectorContent, + ModelSelectorEmpty, + ModelSelectorGroup, + ModelSelectorInput, + ModelSelectorItem, + ModelSelectorList, + ModelSelectorLogo, + ModelSelectorLogoGroup, + ModelSelectorName, + ModelSelectorTrigger, +} from "@/components/ai-elements/model-selector"; +import type { PromptInputMessage } from "@/components/ai-elements/prompt-input"; +import { + PromptInput, + PromptInputActionAddAttachments, + PromptInputActionMenu, + PromptInputActionMenuContent, + PromptInputActionMenuTrigger, + PromptInputBody, + PromptInputButton, + PromptInputFooter, + PromptInputHeader, + PromptInputSubmit, + PromptInputTextarea, + PromptInputTools, + usePromptInputAttachments, +} from "@/components/ai-elements/prompt-input"; +import type { QueueTodo } from "@/components/ai-elements/queue"; +import { + Queue, + QueueItem, + QueueItemAction, + QueueItemActions, + QueueItemContent, + QueueItemDescription, + QueueItemIndicator, + QueueSection, + QueueSectionContent, +} from "@/components/ai-elements/queue"; +import { CheckIcon, GlobeIcon, Trash2 } from "lucide-react"; +import { memo, useCallback, useRef, useState } from "react"; + +const models = [ + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o", + name: "GPT-4o", + providers: ["openai", "azure"], + }, + { + chef: "OpenAI", + chefSlug: "openai", + id: "gpt-4o-mini", + name: "GPT-4o Mini", + providers: ["openai", "azure"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-opus-4-20250514", + name: "Claude 4 Opus", + providers: ["anthropic", "azure", "google", "amazon-bedrock"], + }, + { + chef: "Anthropic", + chefSlug: "anthropic", + id: "claude-sonnet-4-20250514", + name: "Claude 4 Sonnet", + providers: ["anthropic", "azure", "google", "amazon-bedrock"], + }, + { + chef: "Google", + chefSlug: "google", + id: "gemini-2.0-flash-exp", + name: "Gemini 2.0 Flash", + providers: ["google"], + }, +]; + +const SUBMITTING_TIMEOUT = 200; +const STREAMING_TIMEOUT = 2000; + +interface AttachmentItemProps { + attachment: { + id: string; + type: "file"; + filename?: string; + mediaType?: string; + url: string; + }; + onRemove: (id: string) => void; +} + +const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { + const handleRemove = useCallback( + () => onRemove(attachment.id), + [onRemove, attachment.id] + ); + return ( + + + + + ); +}); + +AttachmentItem.displayName = "AttachmentItem"; + +interface TodoItemProps { + todo: QueueTodo; + onRemove: (id: string) => void; +} + +const TodoItem = memo(({ todo, onRemove }: TodoItemProps) => { + const isCompleted = todo.status === "completed"; + const handleRemove = useCallback( + () => onRemove(todo.id), + [onRemove, todo.id] + ); + + return ( + +
+ + + {todo.title} + + + + + + +
+ {todo.description && ( + + {todo.description} + + )} +
+ ); +}); + +TodoItem.displayName = "TodoItem"; + +interface ModelItemProps { + m: (typeof models)[0]; + selectedModel: string; + onSelect: (id: string) => void; +} + +const ModelItem = memo(({ m, selectedModel, onSelect }: ModelItemProps) => { + const handleSelect = useCallback(() => onSelect(m.id), [onSelect, m.id]); + return ( + + + {m.name} + + {m.providers.map((provider) => ( + + ))} + + {selectedModel === m.id ? ( + + ) : ( +
+ )} + + ); +}); + +ModelItem.displayName = "ModelItem"; + +const sampleTodos: QueueTodo[] = [ + { + description: "Complete the README and API docs", + id: "todo-1", + status: "completed", + title: "Write project documentation", + }, + { + id: "todo-2", + status: "pending", + title: "Implement authentication", + }, + { + description: "Resolve crash on settings page", + id: "todo-3", + status: "pending", + title: "Fix bug #42", + }, + { + description: "Unify queue and todo state management", + id: "todo-4", + status: "pending", + title: "Refactor queue logic", + }, + { + description: "Increase test coverage for hooks", + id: "todo-5", + status: "pending", + title: "Add unit tests", + }, +]; + +const PromptInputAttachmentsDisplay = () => { + const attachments = usePromptInputAttachments(); + + const handleRemove = useCallback( + (id: string) => attachments.remove(id), + [attachments] + ); + + if (attachments.files.length === 0) { + return null; + } + + return ( + + {attachments.files.map((attachment) => ( + + ))} + + ); +}; + +const Example = () => { + const [todos, setTodos] = useState(sampleTodos); + + const handleRemoveTodo = useCallback((id: string) => { + setTodos((prev) => prev.filter((todo) => todo.id !== id)); + }, []); + + const [text, setText] = useState(""); + const [model, setModel] = useState(models[0].id); + const [modelSelectorOpen, setModelSelectorOpen] = useState(false); + const [status, setStatus] = useState< + "submitted" | "streaming" | "ready" | "error" + >("ready"); + const timeoutRef = useRef(null); + + const handleTextChange = useCallback( + (e: React.ChangeEvent) => setText(e.target.value), + [] + ); + + const handleModelSelect = useCallback((id: string) => { + setModel(id); + setModelSelectorOpen(false); + }, []); + + const selectedModelData = models.find((m) => m.id === model); + + const stop = () => { + console.log("Stopping request..."); + + // Clear any pending timeouts + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + + setStatus("ready"); + }; + + const handleSubmit = useCallback( + (message: PromptInputMessage) => { + // If currently streaming or submitted, stop instead of submitting + if (status === "streaming" || status === "submitted") { + stop(); + return; + } + + const hasText = Boolean(message.text); + const hasAttachments = Boolean(message.files?.length); + + if (!(hasText || hasAttachments)) { + return; + } + + setStatus("submitted"); + + console.log("Submitting message:", message); + + setTimeout(() => { + setStatus("streaming"); + }, SUBMITTING_TIMEOUT); + + timeoutRef.current = setTimeout(() => { + setStatus("ready"); + timeoutRef.current = null; + }, STREAMING_TIMEOUT); + }, + [status] + ); + + return ( +
+ + {todos.length > 0 && ( + + +
+ {todos.map((todo) => ( + + ))} +
+
+
+ )} +
+ + + + + + + + + + + + + + + + + + Search + + + + + {selectedModelData?.chefSlug && ( + + )} + {selectedModelData?.name && ( + + {selectedModelData.name} + + )} + + + + + + No models found. + {["OpenAI", "Anthropic", "Google"].map((chef) => ( + + {models + .filter((m) => m.chef === chef) + .map((m) => ( + + ))} + + ))} + + + + + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/queue.tsx b/.agents/skills/ai-elements/scripts/queue.tsx new file mode 100644 index 0000000..ffe6988 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/queue.tsx @@ -0,0 +1,295 @@ +"use client"; + +import type { QueueMessage, QueueTodo } from "@/components/ai-elements/queue"; +import { + Queue, + QueueItem, + QueueItemAction, + QueueItemActions, + QueueItemAttachment, + QueueItemContent, + QueueItemDescription, + QueueItemFile, + QueueItemImage, + QueueItemIndicator, + QueueList, + QueueSection, + QueueSectionContent, + QueueSectionLabel, + QueueSectionTrigger, +} from "@/components/ai-elements/queue"; +import { ArrowUp, Trash2 } from "lucide-react"; +import { memo, useCallback, useState } from "react"; + +const sampleMessages: QueueMessage[] = [ + { + id: "msg-1", + parts: [{ text: "How do I set up the project?", type: "text" }], + }, + { + id: "msg-2", + parts: [{ text: "What is the roadmap for Q4?", type: "text" }], + }, + { + id: "msg-3", + parts: [ + { text: "Update the default logo to this png.", type: "text" }, + { + filename: "setup-guide.png", + mediaType: "image/png", + type: "file", + url: "https://github.com/haydenbleasel.png", + }, + ], + }, + { + id: "msg-4", + parts: [{ text: "Please generate a changelog.", type: "text" }], + }, + { + id: "msg-5", + parts: [{ text: "Add dark mode support.", type: "text" }], + }, + { + id: "msg-6", + parts: [{ text: "Optimize database queries.", type: "text" }], + }, + { + id: "msg-7", + parts: [{ text: "Set up CI/CD pipeline.", type: "text" }], + }, +]; + +const sampleTodos: QueueTodo[] = [ + { + description: "Complete the README and API docs", + id: "todo-1", + status: "completed", + title: "Write project documentation", + }, + { + id: "todo-2", + status: "pending", + title: "Implement authentication", + }, + { + description: "Resolve crash on settings page", + id: "todo-3", + status: "pending", + title: "Fix bug #42", + }, + { + description: "Unify queue and todo state management", + id: "todo-4", + status: "pending", + title: "Refactor queue logic", + }, + { + description: "Increase test coverage for hooks", + id: "todo-5", + status: "pending", + title: "Add unit tests", + }, +]; + +interface MessageActionsProps { + messageId: string; + onRemove: (e: React.MouseEvent, id: string) => void; + onSend: (e: React.MouseEvent, id: string) => void; +} + +const MessageActions = memo( + ({ messageId, onRemove, onSend }: MessageActionsProps) => { + const handleRemove = useCallback( + (e: React.MouseEvent) => onRemove(e, messageId), + [onRemove, messageId] + ); + const handleSend = useCallback( + (e: React.MouseEvent) => onSend(e, messageId), + [onSend, messageId] + ); + return ( + + + + + + + + + ); + } +); + +MessageActions.displayName = "MessageActions"; + +interface TodoItemProps { + todo: QueueTodo; + onRemove: (id: string) => void; +} + +const TodoItem = memo(({ todo, onRemove }: TodoItemProps) => { + const isCompleted = todo.status === "completed"; + const handleRemove = useCallback( + () => onRemove(todo.id), + [onRemove, todo.id] + ); + + return ( + +
+ + + {todo.title} + + + + + + +
+ {todo.description && ( + + {todo.description} + + )} +
+ ); +}); + +TodoItem.displayName = "TodoItem"; + +const Example = () => { + const [messages, setMessages] = useState(sampleMessages); + const [todos, setTodos] = useState(sampleTodos); + + const handleRemoveMessage = useCallback((id: string) => { + setMessages((prev) => prev.filter((msg) => msg.id !== id)); + }, []); + + const handleRemoveTodo = useCallback((id: string) => { + setTodos((prev) => prev.filter((todo) => todo.id !== id)); + }, []); + + const handleSendNow = useCallback((id: string) => { + console.log("Send now:", id); + setMessages((prev) => prev.filter((msg) => msg.id !== id)); + }, []); + + const handleMessageRemove = useCallback( + (e: React.MouseEvent, id: string) => { + e.preventDefault(); + e.stopPropagation(); + handleRemoveMessage(id); + }, + [handleRemoveMessage] + ); + + const handleMessageSend = useCallback( + (e: React.MouseEvent, id: string) => { + e.preventDefault(); + e.stopPropagation(); + handleSendNow(id); + }, + [handleSendNow] + ); + + if (messages.length === 0 && todos.length === 0) { + return null; + } + + return ( + + {messages.length > 0 && ( + + + + + + + {messages.map((message) => { + const summary = (() => { + const textParts = message.parts.filter( + (p) => p.type === "text" + ); + const text = textParts + .map((p) => p.text) + .join(" ") + .trim(); + return text || "(queued message)"; + })(); + + const hasFiles = message.parts.some( + (p) => p.type === "file" && p.url + ); + + return ( + +
+ + {summary} + +
+ {hasFiles && ( + + {message.parts + .filter((p) => p.type === "file" && p.url) + .map((file) => { + if ( + file.mediaType?.startsWith("image/") && + file.url + ) { + return ( + + ); + } + return ( + + {file.filename || "file"} + + ); + })} + + )} +
+ ); + })} +
+
+
+ )} + {todos.length > 0 && ( + + + + + + + {todos.map((todo) => ( + + ))} + + + + )} +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/reasoning.tsx b/.agents/skills/ai-elements/scripts/reasoning.tsx new file mode 100644 index 0000000..3f4e8e3 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/reasoning.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { + Reasoning, + ReasoningContent, + ReasoningTrigger, +} from "@/components/ai-elements/reasoning"; +import { useCallback, useEffect, useState } from "react"; + +const reasoningSteps = [ + "Let me think about this problem step by step.", + "\n\nFirst, I need to understand what the user is asking for.", + "\n\nThey want a reasoning component that opens automatically when streaming begins and closes when streaming finishes. The component should be composable and follow existing patterns in the codebase.", + "\n\nThis seems like a collapsible component with state management would be the right approach.", +].join(""); + +const Example = () => { + const [content, setContent] = useState(""); + const [isStreaming, setIsStreaming] = useState(false); + const [currentTokenIndex, setCurrentTokenIndex] = useState(0); + const [tokens, setTokens] = useState([]); + + // Function to chunk text into fake tokens of 3-4 characters + const chunkIntoTokens = useCallback((text: string): string[] => { + const chunks: string[] = []; + let i = 0; + while (i < text.length) { + // Random size between 3-4 + const chunkSize = Math.floor(Math.random() * 2) + 3; + chunks.push(text.slice(i, i + chunkSize)); + i += chunkSize; + } + return chunks; + }, []); + + useEffect(() => { + const tokenizedSteps = chunkIntoTokens(reasoningSteps); + setTokens(tokenizedSteps); + setContent(""); + setCurrentTokenIndex(0); + setIsStreaming(true); + }, [chunkIntoTokens]); + + useEffect(() => { + if (!isStreaming || currentTokenIndex >= tokens.length) { + if (isStreaming) { + setIsStreaming(false); + } + return; + } + + // Faster interval since we're streaming smaller chunks + const timer = setTimeout(() => { + setContent((prev) => prev + tokens[currentTokenIndex]); + setCurrentTokenIndex((prev) => prev + 1); + }, 25); + + return () => clearTimeout(timer); + }, [isStreaming, currentTokenIndex, tokens]); + + return ( +
+ + + {content} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/sandbox.tsx b/.agents/skills/ai-elements/scripts/sandbox.tsx new file mode 100644 index 0000000..12df32d --- /dev/null +++ b/.agents/skills/ai-elements/scripts/sandbox.tsx @@ -0,0 +1,178 @@ +"use client"; + +import { CodeBlock, CodeBlockCopyButton } from "@/components/ai-elements/code-block"; +import { + Sandbox, + SandboxContent, + SandboxHeader, + SandboxTabContent, + SandboxTabs, + SandboxTabsBar, + SandboxTabsList, + SandboxTabsTrigger, +} from "@/components/ai-elements/sandbox"; +import { + StackTrace, + StackTraceActions, + StackTraceContent, + StackTraceCopyButton, + StackTraceError, + StackTraceErrorMessage, + StackTraceErrorType, + StackTraceExpandButton, + StackTraceFrames, + StackTraceHeader, +} from "@/components/ai-elements/stack-trace"; +import { Button } from "@/components/ui/button"; +import type { ToolUIPart } from "ai"; +import { memo, useCallback, useState } from "react"; + +const code = `import math + +def calculate_primes(limit): + """Find all prime numbers up to a given limit using Sieve of Eratosthenes.""" + sieve = [True] * (limit + 1) + sieve[0] = sieve[1] = False + + for i in range(2, int(math.sqrt(limit)) + 1): + if sieve[i]: + for j in range(i * i, limit + 1, i): + sieve[j] = False + + return [i for i, is_prime in enumerate(sieve) if is_prime] + +if __name__ == "__main__": + primes = calculate_primes(50) + print(f"Found {len(primes)} prime numbers up to 50:") + print(primes)`; + +const outputs: Record = { + "input-available": undefined, + "input-streaming": undefined, + "output-available": `Found 15 prime numbers up to 50: +[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]`, + "output-error": `TypeError: Cannot read properties of undefined (reading 'map') + at calculatePrimes (/src/utils/primes.ts:15:23) + at runCalculation (/src/components/Calculator.tsx:42:12) + at onClick (/src/components/Button.tsx:18:5) + at HTMLButtonElement.dispatch (node_modules/react-dom/cjs/react-dom.development.js:3456:9) + at node_modules/react-dom/cjs/react-dom.development.js:4245:12`, +}; + +const states: ToolUIPart["state"][] = [ + "input-streaming", + "input-available", + "output-available", + "output-error", +]; + +interface StateButtonProps { + s: ToolUIPart["state"]; + currentState: ToolUIPart["state"]; + onStateChange: (state: ToolUIPart["state"]) => void; +} + +const StateButton = memo( + ({ s, currentState, onStateChange }: StateButtonProps) => { + const handleClick = useCallback(() => onStateChange(s), [onStateChange, s]); + return ( + + ); + } +); + +StateButton.displayName = "StateButton"; + +const Example = () => { + const [state, setState] = useState("output-available"); + + const handleStateChange = useCallback((s: ToolUIPart["state"]) => { + setState(s); + }, []); + + return ( +
+
+ {states.map((s) => ( + + ))} +
+ + + + + + + + Code + Output + + + + + + + + + {state === "output-error" ? ( + + + + + + + + + + + + + + + + ) : ( + + + + )} + + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/schema-display-basic.tsx b/.agents/skills/ai-elements/scripts/schema-display-basic.tsx new file mode 100644 index 0000000..9c31864 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/schema-display-basic.tsx @@ -0,0 +1,9 @@ +"use client"; + +import { SchemaDisplay } from "@/components/ai-elements/schema-display"; + +const Example = () => ( + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/schema-display-body.tsx b/.agents/skills/ai-elements/scripts/schema-display-body.tsx new file mode 100644 index 0000000..49ab5f2 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/schema-display-body.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { SchemaDisplay } from "@/components/ai-elements/schema-display"; + +const Example = () => ( + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/schema-display-nested.tsx b/.agents/skills/ai-elements/scripts/schema-display-nested.tsx new file mode 100644 index 0000000..1748a21 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/schema-display-nested.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { SchemaDisplay } from "@/components/ai-elements/schema-display"; + +const Example = () => ( + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/schema-display-params.tsx b/.agents/skills/ai-elements/scripts/schema-display-params.tsx new file mode 100644 index 0000000..1b3b1bc --- /dev/null +++ b/.agents/skills/ai-elements/scripts/schema-display-params.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { SchemaDisplay } from "@/components/ai-elements/schema-display"; + +const Example = () => ( + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/schema-display.tsx b/.agents/skills/ai-elements/scripts/schema-display.tsx new file mode 100644 index 0000000..30fab33 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/schema-display.tsx @@ -0,0 +1,110 @@ +"use client"; + +import { + SchemaDisplay, + SchemaDisplayContent, + SchemaDisplayDescription, + SchemaDisplayHeader, + SchemaDisplayMethod, + SchemaDisplayParameters, + SchemaDisplayPath, + SchemaDisplayRequest, + SchemaDisplayResponse, +} from "@/components/ai-elements/schema-display"; + +const Example = () => ( + + +
+ + +
+
+ + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/shimmer-duration.tsx b/.agents/skills/ai-elements/scripts/shimmer-duration.tsx new file mode 100644 index 0000000..5fe3720 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/shimmer-duration.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { Shimmer } from "@/components/ai-elements/shimmer"; + +const Example = () => ( +
+
+

Fast (1 second)

+ Loading quickly... +
+ +
+

Default (2 seconds)

+ Loading at normal speed... +
+ +
+

Slow (4 seconds)

+ Loading slowly... +
+ +
+

+ Very Slow (6 seconds) +

+ Loading very slowly... +
+
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/shimmer-elements.tsx b/.agents/skills/ai-elements/scripts/shimmer-elements.tsx new file mode 100644 index 0000000..74da859 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/shimmer-elements.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { Shimmer } from "@/components/ai-elements/shimmer"; + +const Example = () => ( +
+
+

+ As paragraph (default) +

+ This is rendered as a paragraph +
+ +
+

As heading

+ + Large Heading with Shimmer + +
+ +
+

As span (inline)

+
+ Processing your request{" "} + + with AI magic + + ... +
+
+ +
+

+ As div with custom styling +

+ + Custom styled shimmer text + +
+
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/shimmer.tsx b/.agents/skills/ai-elements/scripts/shimmer.tsx new file mode 100644 index 0000000..1032a9a --- /dev/null +++ b/.agents/skills/ai-elements/scripts/shimmer.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { Shimmer } from "@/components/ai-elements/shimmer"; + +const Example = () => ( +
+ This text has a shimmer effect + + Large Heading + + + Slower shimmer with wider spread + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/snippet-plain.tsx b/.agents/skills/ai-elements/scripts/snippet-plain.tsx new file mode 100644 index 0000000..8c3bb7b --- /dev/null +++ b/.agents/skills/ai-elements/scripts/snippet-plain.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { + Snippet, + SnippetAddon, + SnippetCopyButton, + SnippetInput, +} from "@/components/ai-elements/snippet"; + +const Example = () => ( + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/snippet.tsx b/.agents/skills/ai-elements/scripts/snippet.tsx new file mode 100644 index 0000000..5cbd560 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/snippet.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { + Snippet, + SnippetAddon, + SnippetCopyButton, + SnippetInput, + SnippetText, +} from "@/components/ai-elements/snippet"; + +const Example = () => ( +
+ + + $ + + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/sources-custom.tsx b/.agents/skills/ai-elements/scripts/sources-custom.tsx new file mode 100644 index 0000000..f6d07c4 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/sources-custom.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { + Source, + Sources, + SourcesContent, + SourcesTrigger, +} from "@/components/ai-elements/sources"; +import { ChevronDownIcon, ExternalLinkIcon } from "lucide-react"; + +const sources = [ + { href: "https://stripe.com/docs/api", title: "Stripe API Documentation" }, + { href: "https://docs.github.com/en/rest", title: "GitHub REST API" }, + { + href: "https://docs.aws.amazon.com/sdk-for-javascript/", + title: "AWS SDK for JavaScript", + }, +]; + +const Example = () => ( +
+ + +

Using {sources.length} citations

+ +
+ + {sources.map((source) => ( + + {source.title} + + + ))} + +
+
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/sources.tsx b/.agents/skills/ai-elements/scripts/sources.tsx new file mode 100644 index 0000000..b05e811 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/sources.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { + Source, + Sources, + SourcesContent, + SourcesTrigger, +} from "@/components/ai-elements/sources"; + +const sources = [ + { href: "https://stripe.com/docs/api", title: "Stripe API Documentation" }, + { href: "https://docs.github.com/en/rest", title: "GitHub REST API" }, + { + href: "https://docs.aws.amazon.com/sdk-for-javascript/", + title: "AWS SDK for JavaScript", + }, +]; + +const Example = () => ( +
+ + + + {sources.map((source) => ( + + ))} + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/speech-input.tsx b/.agents/skills/ai-elements/scripts/speech-input.tsx new file mode 100644 index 0000000..a7d5706 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/speech-input.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { SpeechInput } from "@/components/ai-elements/speech-input"; +import { useCallback, useState } from "react"; + +/** + * Fallback handler for browsers that don't support Web Speech API (Firefox, Safari). + * This function receives recorded audio and should send it to a transcription service. + * Example uses OpenAI Whisper API - replace with your preferred service. + */ +const handleAudioRecorded = async (audioBlob: Blob): Promise => { + const formData = new FormData(); + formData.append("file", audioBlob, "audio.webm"); + formData.append("model", "whisper-1"); + + const response = await fetch( + "https://api.openai.com/v1/audio/transcriptions", + { + body: formData, + headers: { + Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_API_KEY}`, + }, + method: "POST", + } + ); + + if (!response.ok) { + throw new Error("Transcription failed"); + } + + const data = await response.json(); + return data.text; +}; + +const Example = () => { + const [transcript, setTranscript] = useState(""); + + const handleTranscriptionChange = useCallback((text: string) => { + setTranscript((prev) => { + const newText = prev ? `${prev} ${text}` : text; + return newText; + }); + }, []); + + const handleClear = useCallback(() => { + setTranscript(""); + }, []); + + return ( +
+
+ + {transcript && ( + + )} +
+ {transcript ? ( +
+

+ Transcript: +

+

{transcript}

+
+ ) : ( +

+ Click the microphone to start speaking +

+ )} +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/stack-trace-collapsed.tsx b/.agents/skills/ai-elements/scripts/stack-trace-collapsed.tsx new file mode 100644 index 0000000..29cfed6 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/stack-trace-collapsed.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { + StackTrace, + StackTraceActions, + StackTraceContent, + StackTraceCopyButton, + StackTraceError, + StackTraceErrorMessage, + StackTraceErrorType, + StackTraceExpandButton, + StackTraceFrames, + StackTraceHeader, +} from "@/components/ai-elements/stack-trace"; + +const errorString = `TypeError: Cannot read properties of undefined (reading 'map') + at UserList (/app/src/components/UserList.tsx:15:23) + at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18) + at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17811:13)`; + +const Example = () => ( + + + + + + + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/stack-trace-no-internal.tsx b/.agents/skills/ai-elements/scripts/stack-trace-no-internal.tsx new file mode 100644 index 0000000..9a68181 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/stack-trace-no-internal.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { + StackTrace, + StackTraceActions, + StackTraceContent, + StackTraceCopyButton, + StackTraceError, + StackTraceErrorMessage, + StackTraceErrorType, + StackTraceExpandButton, + StackTraceFrames, + StackTraceHeader, +} from "@/components/ai-elements/stack-trace"; + +const errorString = `TypeError: Cannot read properties of undefined (reading 'map') + at UserList (/app/src/components/UserList.tsx:15:23) + at App (/app/src/App.tsx:42:5) + at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18) + at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17811:13) + at beginWork (node_modules/react-dom/cjs/react-dom.development.js:19049:16)`; + +const Example = () => ( + + + + + + + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/stack-trace.tsx b/.agents/skills/ai-elements/scripts/stack-trace.tsx new file mode 100644 index 0000000..f33488b --- /dev/null +++ b/.agents/skills/ai-elements/scripts/stack-trace.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { + StackTrace, + StackTraceActions, + StackTraceContent, + StackTraceCopyButton, + StackTraceError, + StackTraceErrorMessage, + StackTraceErrorType, + StackTraceExpandButton, + StackTraceFrames, + StackTraceHeader, +} from "@/components/ai-elements/stack-trace"; + +const handleFilePathClick = (path: string, line: number, col: number) => { + console.log(`Open file: ${path}:${line}:${col}`); +}; + +const handleCopy = () => { + console.log("Stack trace copied"); +}; + +const sampleStackTrace = `TypeError: Cannot read properties of undefined (reading 'map') + at UserList (/app/components/UserList.tsx:15:23) + at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18) + at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17811:13) + at beginWork (node_modules/react-dom/cjs/react-dom.development.js:19049:16) + at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:3945:14) + at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:3994:16) + at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:4056:31) + at beginWork$1 (node_modules/react-dom/cjs/react-dom.development.js:23964:7) + at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:22776:12) + at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:22707:5)`; + +const Example = () => ( + + + + + + + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/suggestion-input.tsx b/.agents/skills/ai-elements/scripts/suggestion-input.tsx new file mode 100644 index 0000000..eaf0120 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/suggestion-input.tsx @@ -0,0 +1,144 @@ +"use client"; + +import type { PromptInputMessage } from "@/components/ai-elements/prompt-input"; +import { + PromptInput, + PromptInputButton, + PromptInputFooter, + PromptInputSelect, + PromptInputSelectContent, + PromptInputSelectItem, + PromptInputSelectTrigger, + PromptInputSelectValue, + PromptInputSubmit, + PromptInputTextarea, + PromptInputTools, +} from "@/components/ai-elements/prompt-input"; +import { Suggestion, Suggestions } from "@/components/ai-elements/suggestion"; +import { GlobeIcon, MicIcon, PlusIcon, SendIcon } from "lucide-react"; +import { nanoid } from "nanoid"; +import { memo, useCallback, useState } from "react"; + +const suggestions: { key: string; value: string }[] = [ + { key: nanoid(), value: "What are the latest trends in AI?" }, + { key: nanoid(), value: "How does machine learning work?" }, + { key: nanoid(), value: "Explain quantum computing" }, + { key: nanoid(), value: "Best practices for React development" }, + { key: nanoid(), value: "Tell me about TypeScript benefits" }, + { key: nanoid(), value: "How to optimize database queries?" }, + { key: nanoid(), value: "What is the difference between SQL and NoSQL?" }, + { key: nanoid(), value: "Explain cloud computing basics" }, +]; + +const models = [ + { id: "gpt-4", name: "GPT-4" }, + { id: "gpt-3.5-turbo", name: "GPT-3.5 Turbo" }, + { id: "claude-2", name: "Claude 2" }, + { id: "claude-instant", name: "Claude Instant" }, + { id: "palm-2", name: "PaLM 2" }, + { id: "llama-2-70b", name: "Llama 2 70B" }, + { id: "llama-2-13b", name: "Llama 2 13B" }, + { id: "cohere-command", name: "Command" }, + { id: "mistral-7b", name: "Mistral 7B" }, +]; + +const handleSubmit = (message: PromptInputMessage) => { + const hasText = Boolean(message.text); + const hasAttachments = Boolean(message.files?.length); + + if (!(hasText || hasAttachments)) { + return; + } + + console.log("Submitted message:", message.text || "Sent with attachments"); + console.log("Attached files:", message.files); +}; + +interface SuggestionItemProps { + suggestion: { key: string; value: string }; + onSuggestionClick: (value: string) => void; +} + +const SuggestionItem = memo( + ({ suggestion, onSuggestionClick }: SuggestionItemProps) => { + const handleClick = useCallback( + () => onSuggestionClick(suggestion.value), + [onSuggestionClick, suggestion.value] + ); + return ( + + ); + } +); + +SuggestionItem.displayName = "SuggestionItem"; + +const Example = () => { + const [model, setModel] = useState(models[0].id); + const [text, setText] = useState(""); + + const handleSuggestionClick = useCallback((suggestion: string) => { + setText(suggestion); + }, []); + + const handleTextChange = useCallback( + (e: React.ChangeEvent) => setText(e.target.value), + [] + ); + + return ( +
+ + {suggestions.map((suggestion) => ( + + ))} + + + + + + + + + + + + + + Search + + + + + + + {models.map((m) => ( + + {m.name} + + ))} + + + + + + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/suggestion.tsx b/.agents/skills/ai-elements/scripts/suggestion.tsx new file mode 100644 index 0000000..b830f5a --- /dev/null +++ b/.agents/skills/ai-elements/scripts/suggestion.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { Suggestion, Suggestions } from "@/components/ai-elements/suggestion"; + +const suggestions = [ + "What are the latest trends in AI?", + "How does machine learning work?", + "Explain quantum computing", + "Best practices for React development", + "Tell me about TypeScript benefits", + "How to optimize database queries?", + "What is the difference between SQL and NoSQL?", + "Explain cloud computing basics", +]; + +const handleSuggestionClick = (suggestion: string) => { + console.log("Selected suggestion:", suggestion); +}; + +const Example = () => ( + + {suggestions.map((suggestion) => ( + + ))} + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/task.tsx b/.agents/skills/ai-elements/scripts/task.tsx new file mode 100644 index 0000000..a4fbb9a --- /dev/null +++ b/.agents/skills/ai-elements/scripts/task.tsx @@ -0,0 +1,59 @@ +"use client"; + +import { SiReact } from "@icons-pack/react-simple-icons"; +import { + Task, + TaskContent, + TaskItem, + TaskItemFile, + TaskTrigger, +} from "@/components/ai-elements/task"; +import { nanoid } from "nanoid"; +import type { ReactNode } from "react"; + +const Example = () => { + const tasks: { key: string; value: ReactNode }[] = [ + { key: nanoid(), value: 'Searching "app/page.tsx, components structure"' }, + { + key: nanoid(), + value: ( + + Read + + + page.tsx + + + ), + }, + { key: nanoid(), value: "Scanning 52 files" }, + { key: nanoid(), value: "Scanning 2 files" }, + { + key: nanoid(), + value: ( + + Reading files + + + layout.tsx + + + ), + }, + ]; + + return ( +
+ + + + {tasks.map((task) => ( + {task.value} + ))} + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/terminal-basic.tsx b/.agents/skills/ai-elements/scripts/terminal-basic.tsx new file mode 100644 index 0000000..c477bed --- /dev/null +++ b/.agents/skills/ai-elements/scripts/terminal-basic.tsx @@ -0,0 +1,7 @@ +"use client"; + +import { Terminal } from "@/components/ai-elements/terminal"; + +const Example = () => ; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/terminal-clear.tsx b/.agents/skills/ai-elements/scripts/terminal-clear.tsx new file mode 100644 index 0000000..e480e1a --- /dev/null +++ b/.agents/skills/ai-elements/scripts/terminal-clear.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { Terminal } from "@/components/ai-elements/terminal"; +import { useCallback, useState } from "react"; + +const initialOutput = `\u001B[36m$\u001B[0m npm run build +Building project... +\u001B[32m✓\u001B[0m Compiled successfully +\u001B[32m✓\u001B[0m Bundle size: 124kb`; + +const Example = () => { + const [output, setOutput] = useState(initialOutput); + + const handleClear = useCallback(() => setOutput(""), []); + + return ; +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/terminal-streaming.tsx b/.agents/skills/ai-elements/scripts/terminal-streaming.tsx new file mode 100644 index 0000000..f0502d0 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/terminal-streaming.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { Terminal } from "@/components/ai-elements/terminal"; +import { useEffect, useState } from "react"; + +const lines = [ + "\u001B[36m$\u001B[0m npm install", + "Installing dependencies...", + "\u001B[32m✓\u001B[0m react@19.0.0", + "\u001B[32m✓\u001B[0m typescript@5.0.0", + "\u001B[32m✓\u001B[0m vite@5.0.0", + "", + "\u001B[32mDone!\u001B[0m Installed 3 packages in 1.2s", +]; + +const Example = () => { + const [output, setOutput] = useState(""); + const [isStreaming, setIsStreaming] = useState(true); + + useEffect(() => { + let lineIndex = 0; + const interval = setInterval(() => { + if (lineIndex < lines.length) { + setOutput((prev) => prev + (prev ? "\n" : "") + lines[lineIndex]); + lineIndex += 1; + } else { + setIsStreaming(false); + clearInterval(interval); + } + }, 500); + + return () => clearInterval(interval); + }, []); + + return ; +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/terminal.tsx b/.agents/skills/ai-elements/scripts/terminal.tsx new file mode 100644 index 0000000..43227ce --- /dev/null +++ b/.agents/skills/ai-elements/scripts/terminal.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { + Terminal, + TerminalActions, + TerminalClearButton, + TerminalContent, + TerminalCopyButton, + TerminalHeader, + TerminalStatus, + TerminalTitle, +} from "@/components/ai-elements/terminal"; +import { useCallback, useEffect, useState } from "react"; + +const handleTerminalCopy = () => { + console.log("Copied!"); +}; + +const ansiOutput = `\u001B[32m✓\u001B[0m Compiled successfully in 1.2s + +\u001B[1m\u001B[34minfo\u001B[0m - Collecting page data... +\u001B[1m\u001B[34minfo\u001B[0m - Generating static pages (0/3) +\u001B[32m✓\u001B[0m Generated static pages (3/3) + +\u001B[1m\u001B[33mwarn\u001B[0m - Using \u001B[1mexperimental\u001B[0m server actions + +\u001B[36mRoute (app)\u001B[0m \u001B[36mSize\u001B[0m \u001B[36mFirst Load JS\u001B[0m +\u001B[37m┌ ○ /\u001B[0m \u001B[32m5.2 kB\u001B[0m \u001B[32m87.3 kB\u001B[0m +\u001B[37m├ ○ /about\u001B[0m \u001B[32m2.1 kB\u001B[0m \u001B[32m84.2 kB\u001B[0m +\u001B[37m└ ○ /contact\u001B[0m \u001B[32m3.8 kB\u001B[0m \u001B[32m85.9 kB\u001B[0m + +\u001B[32m✓\u001B[0m Build completed successfully! +\u001B[90mTotal time: 3.45s\u001B[0m +`; + +const Example = () => { + const [output, setOutput] = useState(""); + const [isStreaming, setIsStreaming] = useState(true); + + useEffect(() => { + let index = 0; + const interval = setInterval(() => { + if (index < ansiOutput.length) { + setOutput(ansiOutput.slice(0, index + 10)); + index += 10; + } else { + setIsStreaming(false); + clearInterval(interval); + } + }, 20); + + return () => clearInterval(interval); + }, []); + + const handleClear = useCallback(() => { + setOutput(""); + setIsStreaming(false); + }, []); + + return ( + + + Build Output +
+ + + + + +
+
+ +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/test-results-basic.tsx b/.agents/skills/ai-elements/scripts/test-results-basic.tsx new file mode 100644 index 0000000..d61f551 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/test-results-basic.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { + TestResults, + TestResultsDuration, + TestResultsHeader, + TestResultsSummary, +} from "@/components/ai-elements/test-results"; + +const Example = () => ( + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/test-results-errors.tsx b/.agents/skills/ai-elements/scripts/test-results-errors.tsx new file mode 100644 index 0000000..37f4e54 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/test-results-errors.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { + Test, + TestError, + TestErrorMessage, + TestErrorStack, + TestResults, + TestResultsContent, + TestResultsHeader, + TestResultsSummary, + TestSuite, + TestSuiteContent, + TestSuiteName, +} from "@/components/ai-elements/test-results"; + +const stackTrace = ` at Object. (/app/src/api.test.ts:45:12) + at Module._compile (node:internal/modules/cjs/loader:1369:14) + at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)`; + +const Example = () => ( + + + + + + + + + + + + Expected 200, got 500 + {stackTrace} + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/test-results-suites.tsx b/.agents/skills/ai-elements/scripts/test-results-suites.tsx new file mode 100644 index 0000000..0ca1d2f --- /dev/null +++ b/.agents/skills/ai-elements/scripts/test-results-suites.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { + Test, + TestResults, + TestResultsContent, + TestResultsHeader, + TestResultsSummary, + TestSuite, + TestSuiteContent, + TestSuiteName, +} from "@/components/ai-elements/test-results"; + +const Example = () => ( + + + + + + + + + + + + + + + +); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/test-results.tsx b/.agents/skills/ai-elements/scripts/test-results.tsx new file mode 100644 index 0000000..0e1e852 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/test-results.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { + Test, + TestError, + TestErrorMessage, + TestErrorStack, + TestResults, + TestResultsContent, + TestResultsDuration, + TestResultsHeader, + TestResultsProgress, + TestResultsSummary, + TestSuite, + TestSuiteContent, + TestSuiteName, +} from "@/components/ai-elements/test-results"; + +const Example = () => ( + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + Expected status 200 but received 500 + + + {` at Object. (src/user.test.ts:45:12) + at Promise.then.completed (node_modules/jest-circus/build/utils.js:391:28)`} + + + + + + + + + + + + + + + Connection timed out after 5000ms + + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/tool-input-available.tsx b/.agents/skills/ai-elements/scripts/tool-input-available.tsx new file mode 100644 index 0000000..0c657b2 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/tool-input-available.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { Tool, ToolContent, ToolHeader, ToolInput } from "@/components/ai-elements/tool"; +import { nanoid } from "nanoid"; + +const toolCall = { + errorText: undefined, + input: { + prompt: "A futuristic cityscape at sunset with flying cars", + quality: "high", + resolution: "1024x1024", + style: "digital_art", + }, + output: undefined, + state: "input-available" as const, + toolCallId: nanoid(), + type: "tool-image_generation" as const, +}; + +const Example = () => ( +
+ + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/tool-input-streaming.tsx b/.agents/skills/ai-elements/scripts/tool-input-streaming.tsx new file mode 100644 index 0000000..6f44aa2 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/tool-input-streaming.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { Tool, ToolContent, ToolHeader, ToolInput } from "@/components/ai-elements/tool"; +import { nanoid } from "nanoid"; + +const toolCall = { + errorText: undefined, + input: { + include_snippets: true, + max_results: 10, + query: "latest AI market trends 2024", + }, + output: undefined, + state: "input-streaming" as const, + toolCallId: nanoid(), + type: "tool-web_search" as const, +}; + +const Example = () => ( +
+ + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/tool-output-available.tsx b/.agents/skills/ai-elements/scripts/tool-output-available.tsx new file mode 100644 index 0000000..b6bf058 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/tool-output-available.tsx @@ -0,0 +1,80 @@ +"use client"; + +import { CodeBlock } from "@/components/ai-elements/code-block"; +import { + Tool, + ToolContent, + ToolHeader, + ToolInput, + ToolOutput, +} from "@/components/ai-elements/tool"; +import type { ToolUIPart } from "ai"; +import { nanoid } from "nanoid"; + +const toolCall: ToolUIPart = { + errorText: undefined, + input: { + database: "analytics", + params: ["2024-01-01"], + query: "SELECT COUNT(*) FROM users WHERE created_at >= ?", + }, + output: [ + { + "Created At": "2024-01-15", + Email: "john@example.com", + Name: "John Doe", + "User ID": 1, + }, + { + "Created At": "2024-01-20", + Email: "jane@example.com", + Name: "Jane Smith", + "User ID": 2, + }, + { + "Created At": "2024-02-01", + Email: "bob@example.com", + Name: "Bob Wilson", + "User ID": 3, + }, + { + "Created At": "2024-02-10", + Email: "alice@example.com", + Name: "Alice Brown", + "User ID": 4, + }, + { + "Created At": "2024-02-15", + Email: "charlie@example.com", + Name: "Charlie Davis", + "User ID": 5, + }, + ], + state: "output-available" as const, + toolCallId: nanoid(), + type: "tool-database_query" as const, +}; + +const Example = () => ( +
+ + + + + {toolCall.state === "output-available" && ( + + } + /> + )} + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/tool-output-error.tsx b/.agents/skills/ai-elements/scripts/tool-output-error.tsx new file mode 100644 index 0000000..9261323 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/tool-output-error.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { + Tool, + ToolContent, + ToolHeader, + ToolInput, + ToolOutput, +} from "@/components/ai-elements/tool"; +import type { ToolUIPart } from "ai"; + +const toolCall: ToolUIPart = { + errorText: + "Connection timeout: The request took longer than 5000ms to complete. Please check your network connection and try again.", + input: { + headers: { + Authorization: "Bearer token123", + "Content-Type": "application/json", + }, + method: "GET", + timeout: 5000, + url: "https://api.example.com/data", + }, + output: undefined, + state: "output-error" as const, + toolCallId: "api_request_1", + type: "tool-api_request" as const, +}; + +const Example = () => ( +
+ + + + + {toolCall.state === "output-error" && ( + + )} + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/tool.tsx b/.agents/skills/ai-elements/scripts/tool.tsx new file mode 100644 index 0000000..7d07b46 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/tool.tsx @@ -0,0 +1,222 @@ +"use client"; + +import { + Confirmation, + ConfirmationAccepted, + ConfirmationAction, + ConfirmationActions, + ConfirmationRejected, + ConfirmationRequest, + ConfirmationTitle, +} from "@/components/ai-elements/confirmation"; +import { + Tool, + ToolContent, + ToolHeader, + ToolInput, + ToolOutput, +} from "@/components/ai-elements/tool"; +import type { ToolUIPart } from "ai"; +import { CheckIcon, XIcon } from "lucide-react"; +import { nanoid } from "nanoid"; + +const handleReject = () => { + // In production, call addConfirmationResponse +}; + +const handleAccept = () => { + // In production, call addConfirmationResponse +}; + +const toolCall: ToolUIPart = { + errorText: undefined, + input: { + database: "analytics", + params: ["2024-01-01"], + query: "SELECT COUNT(*) FROM users WHERE created_at >= ?", + }, + output: `| User ID | Name | Email | Created At | +|---------|------|-------|------------| +| 1 | John Doe | john@example.com | 2024-01-15 | +| 2 | Jane Smith | jane@example.com | 2024-01-20 | +| 3 | Bob Wilson | bob@example.com | 2024-02-01 | +| 4 | Alice Brown | alice@example.com | 2024-02-10 | +| 5 | Charlie Davis | charlie@example.com | 2024-02-15 |`, + state: "output-available" as const, + toolCallId: nanoid(), + type: "tool-database_query" as const, +}; + +const Example = () => ( +
+ {/* 1. input-streaming: Pending */} + + + + + + + + {/* 2. approval-requested: Awaiting Approval */} + + + + + + + + This tool will execute a query on the production database. + + + + Accepted + + + + Rejected + + + + + Reject + + + Accept + + + + + + + {/* 3. approval-responded: Responded */} + + + + + + + + This tool will execute a query on the production database. + + + + Accepted + + + + Rejected + + + + + + + {/* 4. input-available: Running */} + + + + + + + + {/* 5. output-available: Completed */} + + + + + + + + This tool will execute a query on the production database. + + + + Accepted + + + + Rejected + + + + {toolCall.state === "output-available" && ( + + )} + + + + {/* 6. output-error: Error */} + + + + + + + + + {/* 7. output-denied: Denied */} + + + + + + + + This tool will execute a query on the production database. + + + + Accepted + + + + Rejected: Query could impact production performance + + + + + +
+); + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/transcription.tsx b/.agents/skills/ai-elements/scripts/transcription.tsx new file mode 100644 index 0000000..046fd55 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/transcription.tsx @@ -0,0 +1,290 @@ +"use client"; + +import { + Transcription, + TranscriptionSegment, +} from "@/components/ai-elements/transcription"; +import type { Experimental_TranscriptionResult as TranscriptionResult } from "ai"; +import { useCallback, useRef, useState } from "react"; + +const segments: TranscriptionResult["segments"] = [ + { + endSecond: 0.219, + startSecond: 0.119, + text: "You", + }, + { + endSecond: 0.259, + startSecond: 0.219, + text: " ", + }, + { + endSecond: 0.439, + startSecond: 0.259, + text: "can", + }, + { + endSecond: 0.459, + startSecond: 0.439, + text: " ", + }, + { + endSecond: 0.699, + startSecond: 0.459, + text: "build", + }, + { + endSecond: 0.72, + startSecond: 0.699, + text: " ", + }, + { + endSecond: 0.799, + startSecond: 0.72, + text: "and", + }, + { + endSecond: 0.879, + startSecond: 0.799, + text: " ", + }, + { + endSecond: 1.339, + startSecond: 0.879, + text: "host", + }, + { + endSecond: 1.359, + startSecond: 1.339, + text: " ", + }, + { + endSecond: 1.539, + startSecond: 1.36, + text: "many", + }, + { + endSecond: 1.6, + startSecond: 1.539, + text: " ", + }, + { + endSecond: 1.86, + startSecond: 1.6, + text: "different", + }, + { + endSecond: 1.899, + startSecond: 1.86, + text: " ", + }, + { + endSecond: 2.099, + startSecond: 1.899, + text: "types", + }, + { + endSecond: 2.119, + startSecond: 2.099, + text: " ", + }, + { + endSecond: 2.2, + startSecond: 2.119, + text: "of", + }, + { + endSecond: 2.259, + startSecond: 2.2, + text: " ", + }, + { + endSecond: 2.96, + startSecond: 2.259, + text: "applications", + }, + { + endSecond: 3.479, + startSecond: 2.96, + text: " ", + }, + { + endSecond: 3.699, + startSecond: 3.48, + text: "from", + }, + { + endSecond: 3.779, + startSecond: 3.699, + text: " ", + }, + { + endSecond: 4.099, + startSecond: 3.779, + text: "static", + }, + { + endSecond: 4.179, + startSecond: 4.099, + text: " ", + }, + { + endSecond: 4.519, + startSecond: 4.179, + text: "sites", + }, + { + endSecond: 4.539, + startSecond: 4.519, + text: " ", + }, + { + endSecond: 4.759, + startSecond: 4.539, + text: "with", + }, + { + endSecond: 4.799, + startSecond: 4.759, + text: " ", + }, + { + endSecond: 4.939, + startSecond: 4.799, + text: "your", + }, + { + endSecond: 4.96, + startSecond: 4.939, + text: " ", + }, + { + endSecond: 5.219, + startSecond: 4.96, + text: "favorite", + }, + { + endSecond: 5.319, + startSecond: 5.219, + text: " ", + }, + { + endSecond: 5.939, + startSecond: 5.319, + text: "framework,", + }, + { + endSecond: 5.96, + startSecond: 5.939, + text: " ", + }, + { + endSecond: 6.519, + startSecond: 5.96, + text: "multi-tenant", + }, + { + endSecond: 6.559, + startSecond: 6.519, + text: " ", + }, + { + endSecond: 7.259, + startSecond: 6.559, + text: "applications", + }, + { + endSecond: 7.699, + startSecond: 7.259, + text: " ", + }, + { + endSecond: 7.759, + startSecond: 7.699, + text: "or", + }, + { + endSecond: 7.859, + startSecond: 7.759, + text: " ", + }, + { + endSecond: 8.739, + startSecond: 7.859, + text: "micro-frontends", + }, + { + endSecond: 8.78, + startSecond: 8.739, + text: " ", + }, + { + endSecond: 8.96, + startSecond: 8.78, + text: "to", + }, + { + endSecond: 9.099, + startSecond: 8.96, + text: " ", + }, + { + endSecond: 9.779, + startSecond: 9.099, + text: "AI-powered", + }, + { + endSecond: 9.82, + startSecond: 9.779, + text: " ", + }, + { + endSecond: 10.439, + startSecond: 9.82, + text: "agents.", + }, +]; + +const Example = () => { + const audioRef = useRef(null); + const [currentTime, setCurrentTime] = useState(0); + + const handleSeek = useCallback((time: number) => { + if (audioRef.current) { + audioRef.current.currentTime = time; + } + }, []); + + const handleTimeUpdate = useCallback(() => { + if (audioRef.current) { + setCurrentTime(audioRef.current.currentTime); + } + }, []); + + return ( +
+ {/* biome-ignore lint/a11y/useMediaCaption: "No caption needed" */} + {/* oxlint-disable-next-line eslint-plugin-jsx-a11y(media-has-caption) */} + + + + {(segment, index) => ( + + )} + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/voice-selector.tsx b/.agents/skills/ai-elements/scripts/voice-selector.tsx new file mode 100644 index 0000000..f73f7f0 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/voice-selector.tsx @@ -0,0 +1,245 @@ +"use client"; + +import { + VoiceSelector, + VoiceSelectorAccent, + VoiceSelectorAge, + VoiceSelectorBullet, + VoiceSelectorContent, + VoiceSelectorDescription, + VoiceSelectorEmpty, + VoiceSelectorGender, + VoiceSelectorInput, + VoiceSelectorItem, + VoiceSelectorList, + VoiceSelectorName, + VoiceSelectorPreview, + VoiceSelectorTrigger, +} from "@/components/ai-elements/voice-selector"; +import { Button } from "@/components/ui/button"; +import type { ComponentProps } from "react"; +import { memo, useCallback, useRef, useState } from "react"; + +const voices: { + id: string; + name: string; + description: string; + gender: ComponentProps["value"]; + accent: ComponentProps["value"]; + age: string; + previewUrl: string; +}[] = [ + { + accent: "american", + age: "20-30", + description: "Energetic, Social Media Creator", + gender: "male", + id: "liam", + name: "Liam", + previewUrl: + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2026-01-16T21_16_50_Liam%20-%20Energetic%2C%20Social%20Media%20Creator_pre_sp100_s50_sb75_se0_b_m2.mp3", + }, + { + accent: "american", + age: "30-40", + description: "Dominant, Firm", + gender: "male", + id: "adam", + name: "Adam", + previewUrl: + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2026-01-16T21_17_00_Adam%20-%20Dominant%2C%20Firm_pre_sp100_s50_sb75_se0_b_m2.mp3", + }, + { + accent: "british", + age: "30-40", + description: "Clear, Engaging Educator", + gender: "female", + id: "alice", + name: "Alice", + previewUrl: + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2026-01-16T21_17_09_Alice%20-%20Clear%2C%20Engaging%20Educator_pre_sp100_s50_sb75_se0_b_m2.mp3", + }, + { + accent: "american", + age: "50-60", + description: "Wise, Mature, Balanced", + gender: "male", + id: "bill", + name: "Bill", + previewUrl: + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2026-01-16T21_17_25_Bill%20-%20Wise%2C%20Mature%2C%20Balanced_pre_sp100_s50_sb75_se0_b_m2.mp3", + }, + { + accent: "american", + age: "20-30", + description: "Playful, Bright, Warm", + gender: "female", + id: "jessica", + name: "Jessica", + previewUrl: + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2026-01-16T21_17_50_Jessica%20-%20Playful%2C%20Bright%2C%20Warm_pre_sp100_s50_sb75_se0_b_m2.mp3", + }, + { + accent: "british", + age: "30-40", + description: "Velvety Actress", + gender: "female", + id: "lily", + name: "Lily", + previewUrl: + "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/ElevenLabs_2026-01-16T21_18_03_Lily%20-%20Velvety%20Actress_pre_sp100_s50_sb75_se0_b_m2.mp3", + }, +]; + +interface VoiceItemProps { + voice: (typeof voices)[0]; + playingVoice: string | null; + loadingVoice: string | null; + onSelect: (id: string) => void; + onPreview: (id: string) => void; +} + +const VoiceItem = memo( + ({ + voice, + playingVoice, + loadingVoice, + onSelect, + onPreview, + }: VoiceItemProps) => { + const handleSelect = useCallback( + () => onSelect(voice.id), + [onSelect, voice.id] + ); + const handlePreview = useCallback( + () => onPreview(voice.id), + [onPreview, voice.id] + ); + return ( + + + {voice.name} + {voice.description} + + + + {voice.age} + + + + ); + } +); + +VoiceItem.displayName = "VoiceItem"; + +const Example = () => { + const [open, setOpen] = useState(false); + const [selectedVoice, setSelectedVoice] = useState(null); + const [playingVoice, setPlayingVoice] = useState(null); + const [loadingVoice, setLoadingVoice] = useState(null); + const audioRef = useRef(null); + + const handleSelect = useCallback((voiceId: string) => { + setSelectedVoice(voiceId); + setOpen(false); + }, []); + + const handlePreview = useCallback( + (voiceId: string) => { + const voice = voices.find((v) => v.id === voiceId); + if (!voice) { + return; + } + + // If clicking the same voice that's playing, pause it + if (playingVoice === voiceId) { + audioRef.current?.pause(); + setPlayingVoice(null); + return; + } + + // Stop any currently playing audio + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current = null; + } + + setLoadingVoice(voiceId); + + const audio = new Audio(voice.previewUrl); + audioRef.current = audio; + + audio.addEventListener("canplaythrough", () => { + setLoadingVoice(null); + setPlayingVoice(voiceId); + audio.play(); + }); + + audio.addEventListener("ended", () => { + setPlayingVoice(null); + }); + + audio.addEventListener("error", () => { + setLoadingVoice(null); + setPlayingVoice(null); + }); + + audio.load(); + }, + [playingVoice] + ); + + const selectedVoiceData = voices.find((voice) => voice.id === selectedVoice); + + return ( +
+ + + + + + + + No voices found. + {voices.map((voice) => ( + + ))} + + + +
+ ); +}; + +export default Example; diff --git a/.agents/skills/ai-elements/scripts/web-preview.tsx b/.agents/skills/ai-elements/scripts/web-preview.tsx new file mode 100644 index 0000000..8abbdb2 --- /dev/null +++ b/.agents/skills/ai-elements/scripts/web-preview.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { + WebPreview, + WebPreviewBody, + WebPreviewConsole, + WebPreviewNavigation, + WebPreviewNavigationButton, + WebPreviewUrl, +} from "@/components/ai-elements/web-preview"; +import { + ArrowLeftIcon, + ArrowRightIcon, + ExternalLinkIcon, + Maximize2Icon, + MousePointerClickIcon, + RefreshCcwIcon, +} from "lucide-react"; +import { useCallback, useState } from "react"; + +const handleUrlChange = (url: string) => { + console.log("URL changed to:", url); +}; + +const handleGoBack = () => { + console.log("Go back"); +}; + +const handleGoForward = () => { + console.log("Go forward"); +}; + +const handleReload = () => { + console.log("Reload"); +}; + +const handleSelect = () => { + console.log("Select"); +}; + +const handleOpenInNewTab = () => { + console.log("Open in new tab"); +}; + +const exampleLogs = [ + { + level: "log" as const, + message: "Page loaded successfully", + timestamp: new Date(Date.now() - 10_000), + }, + { + level: "warn" as const, + message: "Deprecated API usage detected", + timestamp: new Date(Date.now() - 5000), + }, + { + level: "error" as const, + message: "Failed to load resource", + timestamp: new Date(), + }, +]; + +const Example = () => { + const [_fullscreen, setFullscreen] = useState(false); + + const handleToggleFullscreen = useCallback( + () => setFullscreen((prev) => !prev), + [] + ); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default Example; diff --git a/.agents/skills/better-auth-best-practices/SKILL.md b/.agents/skills/better-auth-best-practices/SKILL.md new file mode 100644 index 0000000..3e6a4e1 --- /dev/null +++ b/.agents/skills/better-auth-best-practices/SKILL.md @@ -0,0 +1,175 @@ +--- +name: better-auth-best-practices +description: Configure Better Auth server and client, set up database adapters, manage sessions, add plugins, and handle environment variables. Use when users mention Better Auth, betterauth, auth.ts, or need to set up TypeScript authentication with email/password, OAuth, or plugin configuration. +--- + +# Better Auth Integration Guide + +**Always consult [better-auth.com/docs](https://better-auth.com/docs) for code examples and latest API.** + +--- + +## Setup Workflow + +1. Install: `npm install better-auth` +2. Set env vars: `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` +3. Create `auth.ts` with database + config +4. Create route handler for your framework +5. Run `npx @better-auth/cli@latest migrate` +6. Verify: call `GET /api/auth/ok` — should return `{ status: "ok" }` + +--- + +## Quick Reference + +### Environment Variables +- `BETTER_AUTH_SECRET` - Encryption secret (min 32 chars). Generate: `openssl rand -base64 32` +- `BETTER_AUTH_URL` - Base URL (e.g., `https://example.com`) + +Only define `baseURL`/`secret` in config if env vars are NOT set. + +### File Location +CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--config` for custom path. + +### CLI Commands +- `npx @better-auth/cli@latest migrate` - Apply schema (built-in adapter) +- `npx @better-auth/cli@latest generate` - Generate schema for Prisma/Drizzle +- `npx @better-auth/cli mcp --cursor` - Add MCP to AI tools + +**Re-run after adding/changing plugins.** + +--- + +## Core Config Options + +| Option | Notes | +|--------|-------| +| `appName` | Optional display name | +| `baseURL` | Only if `BETTER_AUTH_URL` not set | +| `basePath` | Default `/api/auth`. Set `/` for root. | +| `secret` | Only if `BETTER_AUTH_SECRET` not set | +| `database` | Required for most features. See adapters docs. | +| `secondaryStorage` | Redis/KV for sessions & rate limits | +| `emailAndPassword` | `{ enabled: true }` to activate | +| `socialProviders` | `{ google: { clientId, clientSecret }, ... }` | +| `plugins` | Array of plugins | +| `trustedOrigins` | CSRF whitelist | + +--- + +## Database + +**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance. + +**ORM adapters:** Import from `better-auth/adapters/drizzle`, `better-auth/adapters/prisma`, `better-auth/adapters/mongodb`. + +**Critical:** Better Auth uses adapter model names, NOT underlying table names. If Prisma model is `User` mapping to table `users`, use `modelName: "user"` (Prisma reference), not `"users"`. + +--- + +## Session Management + +**Storage priority:** +1. If `secondaryStorage` defined → sessions go there (not DB) +2. Set `session.storeSessionInDatabase: true` to also persist to DB +3. No database + `cookieCache` → fully stateless mode + +**Cookie cache strategies:** +- `compact` (default) - Base64url + HMAC. Smallest. +- `jwt` - Standard JWT. Readable but signed. +- `jwe` - Encrypted. Maximum security. + +**Key options:** `session.expiresIn` (default 7 days), `session.updateAge` (refresh interval), `session.cookieCache.maxAge`, `session.cookieCache.version` (change to invalidate all sessions). + +--- + +## User & Account Config + +**User:** `user.modelName`, `user.fields` (column mapping), `user.additionalFields`, `user.changeEmail.enabled` (disabled by default), `user.deleteUser.enabled` (disabled by default). + +**Account:** `account.modelName`, `account.accountLinking.enabled`, `account.storeAccountCookie` (for stateless OAuth). + +**Required for registration:** `email` and `name` fields. + +--- + +## Email Flows + +- `emailVerification.sendVerificationEmail` - Must be defined for verification to work +- `emailVerification.sendOnSignUp` / `sendOnSignIn` - Auto-send triggers +- `emailAndPassword.sendResetPassword` - Password reset email handler + +--- + +## Security + +**In `advanced`:** +- `useSecureCookies` - Force HTTPS cookies +- `disableCSRFCheck` - ⚠️ Security risk +- `disableOriginCheck` - ⚠️ Security risk +- `crossSubDomainCookies.enabled` - Share cookies across subdomains +- `ipAddress.ipAddressHeaders` - Custom IP headers for proxies +- `database.generateId` - Custom ID generation or `"serial"`/`"uuid"`/`false` + +**Rate limiting:** `rateLimit.enabled`, `rateLimit.window`, `rateLimit.max`, `rateLimit.storage` ("memory" | "database" | "secondary-storage"). + +--- + +## Hooks + +**Endpoint hooks:** `hooks.before` / `hooks.after` - Array of `{ matcher, handler }`. Use `createAuthMiddleware`. Access `ctx.path`, `ctx.context.returned` (after), `ctx.context.session`. + +**Database hooks:** `databaseHooks.user.create.before/after`, same for `session`, `account`. Useful for adding default values or post-creation actions. + +**Hook context (`ctx.context`):** `session`, `secret`, `authCookies`, `password.hash()`/`verify()`, `adapter`, `internalAdapter`, `generateId()`, `tables`, `baseURL`. + +--- + +## Plugins + +**Import from dedicated paths for tree-shaking:** +``` +import { twoFactor } from "better-auth/plugins/two-factor" +``` +NOT `from "better-auth/plugins"`. + +**Popular plugins:** `twoFactor`, `organization`, `passkey`, `magicLink`, `emailOtp`, `username`, `phoneNumber`, `admin`, `apiKey`, `bearer`, `jwt`, `multiSession`, `sso`, `oauthProvider`, `oidcProvider`, `openAPI`, `genericOAuth`. + +Client plugins go in `createAuthClient({ plugins: [...] })`. + +--- + +## Client + +Import from: `better-auth/client` (vanilla), `better-auth/react`, `better-auth/vue`, `better-auth/svelte`, `better-auth/solid`. + +Key methods: `signUp.email()`, `signIn.email()`, `signIn.social()`, `signOut()`, `useSession()`, `getSession()`, `revokeSession()`, `revokeSessions()`. + +--- + +## Type Safety + +Infer types: `typeof auth.$Infer.Session`, `typeof auth.$Infer.Session.user`. + +For separate client/server projects: `createAuthClient()`. + +--- + +## Common Gotchas + +1. **Model vs table name** - Config uses ORM model name, not DB table name +2. **Plugin schema** - Re-run CLI after adding plugins +3. **Secondary storage** - Sessions go there by default, not DB +4. **Cookie cache** - Custom session fields NOT cached, always re-fetched +5. **Stateless mode** - No DB = session in cookie only, logout on cache expiry +6. **Change email flow** - Sends to current email first, then new email + +--- + +## Resources + +- [Docs](https://better-auth.com/docs) +- [Options Reference](https://better-auth.com/docs/reference/options) +- [LLMs.txt](https://better-auth.com/llms.txt) +- [GitHub](https://github.com/better-auth/better-auth) +- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts) \ No newline at end of file diff --git a/.agents/skills/better-auth-security-best-practices/SKILL.MD b/.agents/skills/better-auth-security-best-practices/SKILL.MD new file mode 100644 index 0000000..5abc5a8 --- /dev/null +++ b/.agents/skills/better-auth-security-best-practices/SKILL.MD @@ -0,0 +1,432 @@ +--- +name: better-auth-security-best-practices +description: Configure rate limiting, manage auth secrets, set up CSRF protection, define trusted origins, secure sessions and cookies, encrypt OAuth tokens, track IP addresses, and implement audit logging for Better Auth. Use when users need to secure their auth setup, prevent brute force attacks, or harden a Better Auth deployment. +--- + +## Secret Management + +### Configuring the Secret + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + secret: process.env.BETTER_AUTH_SECRET, // or via `BETTER_AUTH_SECRET` env +}); +``` + +Better Auth looks for secrets in this order: +1. `options.secret` in your config +2. `BETTER_AUTH_SECRET` environment variable +3. `AUTH_SECRET` environment variable + +### Secret Requirements + +- Rejects default/placeholder secrets in production +- Warns if shorter than 32 characters or entropy below 120 bits +- Generate: `openssl rand -base64 32` +- Never commit secrets to version control + +## Rate Limiting + +Enabled in production by default. Applies to all endpoints. Plugins can override per-endpoint. + +### Default Configuration + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + rateLimit: { + enabled: true, // Default: true in production + window: 10, // Time window in seconds (default: 10) + max: 100, // Max requests per window (default: 100) + }, +}); +``` + +### Storage Options + +Options: `"memory"` (resets on restart, avoid on serverless), `"database"` (persistent), `"secondary-storage"` (Redis, default when available). + +```ts +rateLimit: { + storage: "database", +} +``` + +### Custom Storage + +Implement your own rate limit storage: + +```ts +rateLimit: { + customStorage: { + get: async (key) => { + // Return { count: number, expiresAt: number } or null + }, + set: async (key, data) => { + // Store the rate limit data + }, + }, +} +``` + +### Per-Endpoint Rules + +Sensitive endpoints default to 3 requests per 10 seconds (`/sign-in`, `/sign-up`, `/change-password`, `/change-email`). Override: + +```ts +rateLimit: { + customRules: { + "/api/auth/sign-in/email": { + window: 60, // 1 minute window + max: 5, // 5 attempts + }, + "/api/auth/some-safe-endpoint": false, // Disable rate limiting + }, +} +``` + +## CSRF Protection + +Multi-layer protection: origin header validation, Fetch Metadata checks, and first-login protection. + +### Configuration + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + advanced: { + disableCSRFCheck: false, // Default: false (keep enabled) + }, +}); +``` + +Only disable for testing or with an alternative CSRF mechanism. + +## Trusted Origins + +### Configuring Trusted Origins + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + baseURL: "https://api.example.com", + trustedOrigins: [ + "https://app.example.com", + "https://admin.example.com", + ], +}); +``` + +The `baseURL` origin is automatically trusted. Also configurable via env: `BETTER_AUTH_TRUSTED_ORIGINS=https://app.example.com,https://admin.example.com` + +### Wildcard Patterns + +```ts +trustedOrigins: [ + "*.example.com", // Matches any subdomain + "https://*.example.com", // Protocol-specific wildcard + "exp://192.168.*.*:*/*", // Custom schemes (e.g., Expo) +] +``` + +### Dynamic Trusted Origins + +Compute trusted origins based on the request: + +```ts +trustedOrigins: async (request) => { + // Validate against database, header, etc. + const tenant = getTenantFromRequest(request); + return [`https://${tenant}.myapp.com`]; +} +``` + +Validates `callbackURL`, `redirectTo`, `errorCallbackURL`, `newUserCallbackURL`, and `origin` against trusted origins. Invalid URLs receive 403. + +## Session Security + +### Session Expiration + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + session: { + expiresIn: 60 * 60 * 24 * 7, // 7 days (default) + updateAge: 60 * 60 * 24, // Refresh session every 24 hours (default) + }, +}); +``` + +### Session Caching Strategies + +Cache session data in cookies to reduce database queries: + +```ts +session: { + cookieCache: { + enabled: true, + maxAge: 60 * 5, // 5 minutes + strategy: "compact", // Options: "compact", "jwt", "jwe" + }, +} +``` + +Strategies: `"compact"` (Base64url + HMAC, smallest), `"jwt"` (HS256, standard), `"jwe"` (encrypted, use when session has sensitive data). + +## Cookie Security + +Defaults: `secure: true` (HTTPS/production), `sameSite: "lax"`, `httpOnly: true`, `path: "/"`, prefix `__Secure-`. + +### Custom Cookie Configuration + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + advanced: { + useSecureCookies: true, // Force secure cookies + cookiePrefix: "myapp", // Custom prefix (default: "better-auth") + defaultCookieAttributes: { + sameSite: "strict", // Stricter CSRF protection + path: "/auth", // Limit cookie scope + }, + }, +}); +``` + +### Cross-Subdomain Cookies + +```ts +advanced: { + crossSubDomainCookies: { + enabled: true, + domain: ".example.com", // Note the leading dot + additionalCookies: ["session_token", "session_data"], + }, +} +``` + +Only enable if you need authentication sharing and trust all subdomains. + +## OAuth / Social Provider Security + +PKCE is automatic for all OAuth flows. State tokens are 32-char random strings expiring after 10 minutes. + +### State Parameter Storage + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + account: { + storeStateStrategy: "cookie", // Options: "cookie" (default), "database" + }, +}); +``` + +### Encrypting OAuth Tokens + +```ts +account: { + encryptOAuthTokens: true, // Uses AES-256-GCM +} +``` + +Enable if storing OAuth tokens for API access on behalf of users. Use `skipStateCookieCheck: true` only for mobile apps that cannot maintain cookies. + +## IP-Based Security + +### IP Address Configuration + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + advanced: { + ipAddress: { + ipAddressHeaders: ["x-forwarded-for", "x-real-ip"], // Headers to check + disableIpTracking: false, // Keep enabled for rate limiting + }, + }, +}); +``` + +Set `ipv6Subnet` (128, 64, 48, 32; default 64) to group IPv6 addresses. Enable `trustedProxyHeaders: true` only if behind a trusted reverse proxy. + +## Database Hooks for Security Auditing + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + databaseHooks: { + session: { + create: { + after: async ({ data, ctx }) => { + await auditLog("session.created", { + userId: data.userId, + ip: ctx?.request?.headers.get("x-forwarded-for"), + userAgent: ctx?.request?.headers.get("user-agent"), + }); + }, + }, + delete: { + before: async ({ data }) => { + await auditLog("session.revoked", { sessionId: data.id }); + }, + }, + }, + user: { + update: { + after: async ({ data, oldData }) => { + if (oldData?.email !== data.email) { + await auditLog("user.email_changed", { + userId: data.id, + oldEmail: oldData?.email, + newEmail: data.email, + }); + } + }, + }, + }, + account: { + create: { + after: async ({ data }) => { + await auditLog("account.linked", { + userId: data.userId, + provider: data.providerId, + }); + }, + }, + }, + }, +}); +``` + +Return `false` from a `before` hook to prevent an operation. + +## Background Tasks + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + advanced: { + backgroundTasks: { + handler: (promise) => { + // Platform-specific handler + // Vercel: waitUntil(promise) + // Cloudflare: ctx.waitUntil(promise) + waitUntil(promise); + }, + }, + }, +}); +``` + +Ensures operations like sending emails don't affect response timing. + +## Account Enumeration Prevention + +Built-in: consistent response messages, dummy operations on invalid requests, background email sending. Return generic error messages ("Invalid credentials") rather than specific ones ("User not found"). + +## Complete Security Configuration Example + +```ts +import { betterAuth } from "better-auth"; + +export const auth = betterAuth({ + secret: process.env.BETTER_AUTH_SECRET, + baseURL: "https://api.example.com", + trustedOrigins: [ + "https://app.example.com", + "https://*.preview.example.com", + ], + + // Rate limiting + rateLimit: { + enabled: true, + storage: "secondary-storage", + customRules: { + "/api/auth/sign-in/email": { window: 60, max: 5 }, + "/api/auth/sign-up/email": { window: 60, max: 3 }, + }, + }, + + // Session security + session: { + expiresIn: 60 * 60 * 24 * 7, // 7 days + updateAge: 60 * 60 * 24, // 24 hours + freshAge: 60 * 60, // 1 hour for sensitive actions + cookieCache: { + enabled: true, + maxAge: 300, + strategy: "jwe", // Encrypted session data + }, + }, + + // OAuth security + account: { + encryptOAuthTokens: true, + storeStateStrategy: "cookie", + }, + + + // Advanced settings + advanced: { + useSecureCookies: true, + cookiePrefix: "myapp", + defaultCookieAttributes: { + sameSite: "lax", + }, + ipAddress: { + ipAddressHeaders: ["x-forwarded-for"], + ipv6Subnet: 64, + }, + backgroundTasks: { + handler: (promise) => waitUntil(promise), + }, + }, + + // Security auditing + databaseHooks: { + session: { + create: { + after: async ({ data, ctx }) => { + console.log(`New session for user ${data.userId}`); + }, + }, + }, + user: { + update: { + after: async ({ data, oldData }) => { + if (oldData?.email !== data.email) { + console.log(`Email changed for user ${data.id}`); + } + }, + }, + }, + }, +}); +``` + +## Security Checklist + +Before deploying to production: + +- [ ] **Secret**: Use a strong, unique secret (32+ characters, high entropy) +- [ ] **HTTPS**: Ensure `baseURL` uses HTTPS +- [ ] **Trusted Origins**: Configure all valid origins (frontend, mobile apps) +- [ ] **Rate Limiting**: Keep enabled with appropriate limits +- [ ] **CSRF Protection**: Keep enabled (`disableCSRFCheck: false`) +- [ ] **Secure Cookies**: Enabled automatically with HTTPS +- [ ] **OAuth Tokens**: Consider `encryptOAuthTokens: true` if storing tokens +- [ ] **Background Tasks**: Configure for serverless platforms +- [ ] **Audit Logging**: Implement via `databaseHooks` or `hooks` +- [ ] **IP Tracking**: Configure headers if behind a proxy diff --git a/.agents/skills/bklit-ui/SKILL.md b/.agents/skills/bklit-ui/SKILL.md new file mode 100644 index 0000000..cd8ee94 --- /dev/null +++ b/.agents/skills/bklit-ui/SKILL.md @@ -0,0 +1,136 @@ +--- +name: bklit-ui +description: > + Bklit UI charts and data visualization for any project using the @bklit + shadcn registry. Install, compose, theme, and animate charts correctly. + Triggers when working with @bklitui/ui/charts, @bklit components, data + visualization, dashboards, or chart theming. Also invoke manually for + chart tasks. +allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *) +--- + +# Bklit UI + +Composable chart components for React, distributed via the `@bklit` shadcn registry. Charts are installed as source into the user's project. + +> **IMPORTANT:** Run shadcn CLI commands with the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest`. + +## Current Project Context + +```json +!`npx shadcn@latest info --json` +``` + +Use the JSON above for framework, aliases, Tailwind version, installed components, and resolved paths. Confirm the `@bklit` registry is configured before adding charts. + +## Principles + +1. **Install before inventing.** Use `npx shadcn@latest add @bklit/` — charts are registry components, not hand-rolled SVG. +2. **Compose, don't flatten.** Root chart → `Grid` → series → axes → `ChartTooltip`. See [composition.md](./rules/composition.md). +3. **Theme with tokens.** Use `chartCssVars` and `--chart-*` variables — never hardcode one-off colors. See [theming.md](./rules/theming.md). +4. **Read the doc page first.** Each chart has props, data shape, and examples at `https://ui.bklit.com/docs/components/`. +5. **Browse variants.** Gallery: `https://ui.bklit.com/charts/` — Studio: `https://ui.bklit.com/studio?chart=`. + +## Critical Rules + +These rules are **always enforced**. Each links to Incorrect/Correct examples. + +### Composition → [composition.md](./rules/composition.md) + +- **Series and axes live inside the root chart** — `LineChart`, `BarChart`, `AreaChart`, etc. +- **One root per chart** — use `ComposedChart` for mixed series types. +- **Grid before series** so lines/bars render above grid lines. +- **`ChartTooltip` as a chart child** — required for crosshair and hover context. + +### Theming → [theming.md](./rules/theming.md) + +- **Use `chartCssVars`** from `@bklitui/ui/charts` instead of raw `"var(--chart-…)"` strings. +- **Series palette:** `--chart-1` … `--chart-5` for multi-series charts. +- **Tooltip surfaces:** `bg-popover text-popover-foreground` — avoids white-on-white in light mode. + +### Animation → [animation.md](./rules/animation.md) + +- **Default duration ~1100ms** for cartesian enter animations unless the doc specifies otherwise. +- **Replay:** change `revealSignature` or remount with a new `key`. +- **Live charts:** use `paused` on `LiveLineChart` to debug without stopping the rAF loop manually. + +### Tooltips → [tooltips.md](./rules/tooltips.md) + +- **Custom content via `ChartTooltip` `content` prop** or children patterns from docs. +- **`indicatorColor` function** for candlestick / dynamic crosshair colors. +- **Custom indicators:** use `useChart()` — do not track mouse globally outside chart context. + +### Installation → [installation.md](./rules/installation.md) + +- **Require `@bklit` registry** in `components.json`. +- **Install:** `npx shadcn@latest add @bklit/`. +- **Let the CLI install peer dependencies** — do not pin `@visx/*` / `motion` manually unless resolving a conflict. + +## Chart Catalog + +| Slug | Use when | Install | Docs | Gallery | +|------|----------|---------|------|---------| +| `area-chart` | Trends with filled regions under lines | `@bklit/area-chart` | [/docs/components/area-chart](https://ui.bklit.com/docs/components/area-chart) | [/charts/area-chart](https://ui.bklit.com/charts/area-chart) | +| `bar-chart` | Category comparisons, stacked or grouped bars | `@bklit/bar-chart` | [/docs/components/bar-chart](https://ui.bklit.com/docs/components/bar-chart) | [/charts/bar-chart](https://ui.bklit.com/charts/bar-chart) | +| `line-chart` | Time series, multi-line trends, markers | `@bklit/line-chart` | [/docs/components/line-chart](https://ui.bklit.com/docs/components/line-chart) | [/charts/line-chart](https://ui.bklit.com/charts/line-chart) | +| `live-line-chart` | Streaming / real-time data | `@bklit/live-line-chart` | [/docs/components/live-line-chart](https://ui.bklit.com/docs/components/live-line-chart) | [/charts/live-line-chart](https://ui.bklit.com/charts/live-line-chart) | +| `composed-chart` | Mixed bar + line (or similar) on one axis | `@bklit/composed-chart` | [/docs/components/composed-chart](https://ui.bklit.com/docs/components/composed-chart) | [/charts/composed-chart](https://ui.bklit.com/charts/composed-chart) | +| `scatter-chart` | Correlation, distribution, bubble sizing | `@bklit/scatter-chart` | [/docs/components/scatter-chart](https://ui.bklit.com/docs/components/scatter-chart) | [/charts/scatter-chart](https://ui.bklit.com/charts/scatter-chart) | +| `candlestick-chart` | OHLC financial data, brushes | `@bklit/candlestick-chart` | [/docs/components/candlestick-chart](https://ui.bklit.com/docs/components/candlestick-chart) | [/charts/candlestick-chart](https://ui.bklit.com/charts/candlestick-chart) | +| `pie-chart` | Part-to-whole slices | `@bklit/pie-chart` | [/docs/components/pie-chart](https://ui.bklit.com/docs/components/pie-chart) | [/charts/pie-chart](https://ui.bklit.com/charts/pie-chart) | +| `ring-chart` | Donut / ring KPIs | `@bklit/ring-chart` | [/docs/components/ring-chart](https://ui.bklit.com/docs/components/ring-chart) | [/charts/ring-chart](https://ui.bklit.com/charts/ring-chart) | +| `radar-chart` | Multi-axis comparison | `@bklit/radar-chart` | [/docs/components/radar-chart](https://ui.bklit.com/docs/components/radar-chart) | [/charts/radar-chart](https://ui.bklit.com/charts/radar-chart) | +| `gauge-chart` | Single-value KPI dial | `@bklit/gauge-chart` | [/docs/components/gauge-chart](https://ui.bklit.com/docs/components/gauge-chart) | [/charts/gauge-chart](https://ui.bklit.com/charts/gauge-chart) | +| `funnel-chart` | Stage conversion funnels | `@bklit/funnel-chart` | [/docs/components/funnel-chart](https://ui.bklit.com/docs/components/funnel-chart) | [/charts/funnel-chart](https://ui.bklit.com/charts/funnel-chart) | +| `sankey-chart` | Flow between nodes | `@bklit/sankey-chart` | [/docs/components/sankey-chart](https://ui.bklit.com/docs/components/sankey-chart) | [/charts/sankey-chart](https://ui.bklit.com/charts/sankey-chart) | +| `choropleth-chart` | Geo regions colored by value | `@bklit/choropleth-chart` | [/docs/components/choropleth-chart](https://ui.bklit.com/docs/components/choropleth-chart) | [/charts/choropleth-chart](https://ui.bklit.com/charts/choropleth-chart) | + +## Workflow + +1. Run `npx shadcn@latest info --json` — verify `@bklit` registry and aliases. +2. Pick a chart from the catalog (or ask the user what story the data tells). +3. Open the doc URL for data shape and props. +4. If not installed: `npx shadcn@latest add @bklit/`. +5. Compose with grid, series, axes, tooltip — apply theming tokens. +6. Point the user to the gallery or Studio URL for variant inspiration. + +## Quick Reference + +```bash +# Project info +npx shadcn@latest info --json + +# Add a chart +npx shadcn@latest add @bklit/line-chart + +# Search registries (if configured) +npx shadcn@latest search @bklit +``` + +```tsx +import { LineChart, Line, Grid, XAxis, ChartTooltip, chartCssVars } from "@bklitui/ui/charts"; + + + + + + + +``` + +## Utility docs + +- Theming: https://ui.bklit.com/docs/theming +- Grid: https://ui.bklit.com/docs/utility/grid +- Legend: https://ui.bklit.com/docs/utility/legend +- Tooltip: https://ui.bklit.com/docs/utility/tooltip +- Custom indicator: https://ui.bklit.com/docs/utility/custom-indicator +- useChart: https://ui.bklit.com/docs/utility/use-chart + +## Detailed References + +- [composition.md](./rules/composition.md) +- [theming.md](./rules/theming.md) +- [animation.md](./rules/animation.md) +- [tooltips.md](./rules/tooltips.md) +- [installation.md](./rules/installation.md) diff --git a/.agents/skills/bklit-ui/rules/animation.md b/.agents/skills/bklit-ui/rules/animation.md new file mode 100644 index 0000000..964488c --- /dev/null +++ b/.agents/skills/bklit-ui/rules/animation.md @@ -0,0 +1,42 @@ +# Animation + +## Defaults + +Most cartesian charts default to ~1100ms enter animation. Bar charts often use staggered reveals (~1.2s total) with easing `cubic-bezier(0.85, 0, 0.15, 1)`. + +## Replay enter animations + +Pass a changing `revealSignature` (or remount the chart with a new `key`) to replay mount animations after prop changes. + +### Correct + +```tsx +const [replayKey, setReplayKey] = useState(0); + + + {/* ... */} + + + +``` + +## Live charts + +- `LiveLineChart` runs its own animation loop — use `paused` to freeze updates for debugging. +- Avoid nesting heavy state updates inside the rAF path; pass stable props where possible. + +## Reduced motion + +Respect `prefers-reduced-motion` when adding custom animation wrappers around charts. Bklit charts handle reduced motion internally for enter transitions. + +## Performance + +- Prefer CSS/SVG-friendly props over re-creating large data arrays every frame. +- For streaming data, append points and trim the window instead of replacing the full array when possible. + +## Inspiration + +Browse animated variants: https://ui.bklit.com/charts/ +Tune motion interactively: https://ui.bklit.com/studio?chart= diff --git a/.agents/skills/bklit-ui/rules/composition.md b/.agents/skills/bklit-ui/rules/composition.md new file mode 100644 index 0000000..ccc9eaa --- /dev/null +++ b/.agents/skills/bklit-ui/rules/composition.md @@ -0,0 +1,57 @@ +# Chart Composition + +## Root + children + +Charts use a composable API. Always wrap series, axes, and overlays inside the root chart component. + +### Incorrect + +```tsx +
+ + +
+``` + +### Correct + +```tsx + + + + + + +``` + +## Axes and grid + +- Put `Grid` before series so lines render on top. +- Use `XAxis` / `YAxis` as siblings inside the root chart — not outside the chart context. +- For live streaming charts, use `LiveXAxis` and `LiveYAxis` inside `LiveLineChart`. + +## Tooltips and markers + +- `ChartTooltip` must be a child of the root chart so it can read hover state. +- Custom tooltip content receives chart context — prefer `useChart()` when building custom indicators. +- `ChartMarkers` and marker content render inside `ChartTooltip` when showing event callouts. + +## Multi-series charts + +- One `Line` / `Bar` / `Area` child per `dataKey`. +- Use `--chart-1` … `--chart-5` or explicit stroke/fill props for series differentiation. +- For combined line + bar, use `ComposedChart` instead of nesting unrelated roots. + +## Data shape + +- Cartesian charts: array of objects; set `xDataKey` on the root (default `"date"`). +- OHLC: use `CandlestickChart` with `{ date, open, high, low, close }`. +- Live line: `{ time, value }` points with a separate `value` prop on the root. +- Sankey / funnel / pie: follow each chart’s doc for node/link or slice shape. + +## Docs + +- Composition patterns: https://ui.bklit.com/docs/components/line-chart +- `useChart` hook: https://ui.bklit.com/docs/utility/use-chart +- Grid: https://ui.bklit.com/docs/utility/grid +- Legend: https://ui.bklit.com/docs/utility/legend diff --git a/.agents/skills/bklit-ui/rules/installation.md b/.agents/skills/bklit-ui/rules/installation.md new file mode 100644 index 0000000..b8afac0 --- /dev/null +++ b/.agents/skills/bklit-ui/rules/installation.md @@ -0,0 +1,74 @@ +# Installation + +## Prerequisites + +Bklit UI is a shadcn registry. Initialize shadcn in the project first: + +```bash +npx shadcn@latest init +``` + +## Registry namespace + +Add the `@bklit` namespace to `components.json` if it is not already present: + +```json +{ + "registries": { + "@bklit": "https://ui.bklit.com/r/{name}.json" + } +} +``` + +New projects scaffolded with Bklit often include this automatically. + +## Install a chart + +```bash +npx shadcn@latest add @bklit/line-chart +``` + +Replace `line-chart` with any chart slug from the catalog. The CLI copies source into your components directory and installs peer dependencies. + +## Verify project context + +```bash +npx shadcn@latest info --json +``` + +Use the JSON to confirm framework, aliases, Tailwind version, and which `@bklit` components are already installed. + +## Import path + +After install, import from the charts entry (path may vary by alias — check `info --json`): + +```tsx +import { LineChart, Line, Grid, XAxis, ChartTooltip } from "@/components/ui/line-chart"; +// or from package re-exports when using the npm package directly: +import { LineChart, Line, Grid, XAxis, ChartTooltip } from "@bklitui/ui/charts"; +``` + +Prefer the import path your project’s shadcn install actually generated. + +## Common peer dependencies + +| Chart | Typical dependencies | +|-------|---------------------| +| line-chart, area-chart | `@visx/curve`, `@visx/shape`, `motion` | +| bar-chart | `@visx/gradient`, `@visx/pattern`, `@visx/shape`, `motion` | +| candlestick-chart | `@visx/scale`, `@visx/shape`, `@visx/responsive`, `d3-array`, `motion` | +| live-line-chart | `@visx/curve`, `@visx/scale`, `@visx/shape`, `@visx/responsive`, `@visx/event`, `d3-array`, `motion` | +| choropleth-chart | `@visx/geo`, `@visx/responsive`, `@visx/zoom`, `d3-geo`, `topojson-client`, `motion` | +| sankey-chart | `@visx/gradient`, `@visx/pattern`, `@visx/responsive`, `@visx/sankey`, `motion` | +| scatter-chart | `d3-scale`, `d3-array`, `motion`, `react-use-measure` | +| pie-chart, ring-chart | `@visx/responsive`, `@visx/shape`, `motion` | +| radar-chart | `@visx/responsive`, `d3-shape`, `motion` | +| funnel-chart | `motion` | +| gauge-chart | `@visx/responsive`, `motion` | + +The CLI installs these when adding a chart — do not guess versions; let `shadcn add` resolve them. + +## Docs + +- Installation: https://ui.bklit.com/docs/installation +- Per-chart install tabs: https://ui.bklit.com/docs/components/ diff --git a/.agents/skills/bklit-ui/rules/theming.md b/.agents/skills/bklit-ui/rules/theming.md new file mode 100644 index 0000000..6bbf8b5 --- /dev/null +++ b/.agents/skills/bklit-ui/rules/theming.md @@ -0,0 +1,55 @@ +# Theming + +## Use chartCssVars + +Prefer the typed `chartCssVars` export over raw CSS variable strings. + +### Incorrect + +```tsx + + +``` + +### Correct + +```tsx +import { chartCssVars } from "@bklitui/ui/charts"; + + + +``` + +## Series colors + +- Multi-series: `var(--chart-1)` through `var(--chart-5)` or theme tokens. +- Line charts: `var(--chart-line-primary)` / `var(--chart-line-secondary)` for default strokes. +- Do not hardcode hex colors unless the design explicitly requires brand colors outside the theme. + +## Tooltip and badge surfaces + +Tooltip boxes and live value badges should use shadcn popover tokens so text stays readable in light and dark mode: + +```tsx +// Tooltip content / badges +className="bg-popover text-popover-foreground" +``` + +## Dark mode + +Override chart variables in `:root` and `.dark` — do not sprinkle `dark:` on individual chart SVG elements. + +```css +:root { + --chart-background: oklch(1 0 0); + --chart-grid: oklch(0.9 0 0); +} +.dark { + --chart-background: oklch(0.145 0.004 285); + --chart-grid: oklch(0.25 0 0); +} +``` + +## Docs + +- Full theming guide: https://ui.bklit.com/docs/theming diff --git a/.agents/skills/bklit-ui/rules/tooltips.md b/.agents/skills/bklit-ui/rules/tooltips.md new file mode 100644 index 0000000..3de29ca --- /dev/null +++ b/.agents/skills/bklit-ui/rules/tooltips.md @@ -0,0 +1,71 @@ +# Tooltips + +## Default tooltip + +Use `ChartTooltip` as a child of the root chart. Defaults include crosshair, dots, and date pill where applicable. + +```tsx + + + + + +``` + +## Custom content + +```tsx + ( +
+
{formattedDate}
+
{point.users as number} users
+
+ )} +/> +``` + +## indicatorColor (candlestick and crosshair) + +Match the crosshair to the hovered datum — e.g. green/red candles: + +```tsx + + (point.close as number) >= (point.open as number) + ? "var(--chart-1)" + : "var(--chart-3)" + } + showDots={false} +/> +``` + +## Custom indicators + +For advanced crosshair/markers, use `useChart()` and the patterns in the custom indicator docs. + +### Incorrect + +```tsx +// Reading mouse position manually outside chart context +const [x, setX] = useState(0); +useEffect(() => { + window.addEventListener("mousemove", ...); +}, []); +``` + +### Correct + +```tsx +import { useChart } from "@bklitui/ui/charts"; + +function CustomIndicator() { + const { tooltipData } = useChart(); + // render from tooltipData +} +``` + +## Docs + +- ChartTooltip: https://ui.bklit.com/docs/utility/tooltip +- Custom indicators: https://ui.bklit.com/docs/utility/custom-indicator diff --git a/.agents/skills/coss-particles/SKILL.md b/.agents/skills/coss-particles/SKILL.md new file mode 100644 index 0000000..19f5c4a --- /dev/null +++ b/.agents/skills/coss-particles/SKILL.md @@ -0,0 +1,737 @@ +--- +name: coss-particles +description: Index of all COSS UI particle examples. Use when implementing UI features to find copy-paste-ready component patterns built on coss primitives. Each particle has a description and a JSON URL for easy installation. +compatibility: Requires coss UI components installed in the project. +license: MIT +metadata: + author: cosscom +--- + +# COSS UI Particles Index + +Particles are copy-paste-ready UI patterns built on [coss](https://coss.com/ui) primitives. Browse them visually at . + +## How to use this skill + +1. Describe the UI you need (e.g. "a form with validation", "a dialog with a form inside", "tabs with icons"). +2. Search this index for matching particles by component type and description. +3. Fetch the JSON URL to get the full source code of the particle. +4. Adapt the particle code to your needs. + +## JSON URL pattern + +Each particle has a JSON manifest at: +``` +https://coss.com/ui/r/.json +``` +For example: `https://coss.com/ui/r/p-accordion-1.json` + +## Source code + +Particle source files live in this repo at `apps/ui/registry/default/particles/`. + +## Updating this index + +Run the generator script from the coss repo root: +```bash +node apps/ui/scripts/generate-particle-index.cjs +``` + +Total: **484 particles** across **52 component types** + +## Component types + +- [accordion](#accordion) (4) +- [alert](#alert) (7) +- [alert-dialog](#alert-dialog) (2) +- [autocomplete](#autocomplete) (15) +- [avatar](#avatar) (14) +- [badge](#badge) (20) +- [breadcrumb](#breadcrumb) (7) +- [button](#button) (40) +- [calendar](#calendar) (24) +- [card](#card) (11) +- [checkbox](#checkbox) (5) +- [checkbox-group](#checkbox-group) (5) +- [collapsible](#collapsible) (1) +- [combobox](#combobox) (18) +- [command](#command) (2) +- [date-picker](#date-picker) (9) +- [dialog](#dialog) (6) +- [drawer](#drawer) (14) +- [empty](#empty) (1) +- [field](#field) (18) +- [fieldset](#fieldset) (1) +- [form](#form) (2) +- [frame](#frame) (4) +- [group](#group) (22) +- [input](#input) (19) +- [input-group](#input-group) (28) +- [kbd](#kbd) (1) +- [menu](#menu) (9) +- [meter](#meter) (4) +- [number-field](#number-field) (11) +- [otp-field](#otp-field) (9) +- [pagination](#pagination) (3) +- [popover](#popover) (3) +- [preview-card](#preview-card) (1) +- [progress](#progress) (3) +- [radio-group](#radio-group) (6) +- [scroll-area](#scroll-area) (5) +- [select](#select) (23) +- [separator](#separator) (1) +- [sheet](#sheet) (3) +- [skeleton](#skeleton) (2) +- [slider](#slider) (23) +- [spinner](#spinner) (1) +- [switch](#switch) (6) +- [table](#table) (8) +- [tabs](#tabs) (13) +- [textarea](#textarea) (15) +- [toast](#toast) (13) +- [toggle](#toggle) (8) +- [toggle-group](#toggle-group) (9) +- [toolbar](#toolbar) (1) +- [tooltip](#tooltip) (4) + +--- + +### accordion + +- Basic accordion | [JSON](https://coss.com/ui/r/p-accordion-1.json) +- Accordion with one panel open | [JSON](https://coss.com/ui/r/p-accordion-2.json) +- Accordion allowing multiple panels open | [JSON](https://coss.com/ui/r/p-accordion-3.json) +- Controlled accordion | [JSON](https://coss.com/ui/r/p-accordion-4.json) + +### alert + +- Basic alert | [JSON](https://coss.com/ui/r/p-alert-1.json) +- Alert with icon | [JSON](https://coss.com/ui/r/p-alert-2.json) +- Alert with icon and action buttons | [JSON](https://coss.com/ui/r/p-alert-3.json) +- Info alert | [JSON](https://coss.com/ui/r/p-alert-4.json) +- Success alert | [JSON](https://coss.com/ui/r/p-alert-5.json) +- Warning alert | [JSON](https://coss.com/ui/r/p-alert-6.json) +- Error alert | [JSON](https://coss.com/ui/r/p-alert-7.json) + +### alert-dialog + +- Alert dialog | [JSON](https://coss.com/ui/r/p-alert-dialog-1.json) +- Alert dialog with bare footer | [JSON](https://coss.com/ui/r/p-alert-dialog-2.json) + +### autocomplete + +- Basic autocomplete | [JSON](https://coss.com/ui/r/p-autocomplete-1.json) +- Disabled autocomplete | [JSON](https://coss.com/ui/r/p-autocomplete-2.json) +- Small autocomplete | [JSON](https://coss.com/ui/r/p-autocomplete-3.json) +- Large autocomplete | [JSON](https://coss.com/ui/r/p-autocomplete-4.json) +- Autocomplete with label | [JSON](https://coss.com/ui/r/p-autocomplete-5.json) +- Autocomplete autofilling the input with the highlighted item | [JSON](https://coss.com/ui/r/p-autocomplete-6.json) +- Autocomplete auto highlighting the first option | [JSON](https://coss.com/ui/r/p-autocomplete-7.json) +- Autocomplete with clear button | [JSON](https://coss.com/ui/r/p-autocomplete-8.json) +- Autocomplete with trigger and clear buttons | [JSON](https://coss.com/ui/r/p-autocomplete-9.json) +- Autocomplete with grouped items | [JSON](https://coss.com/ui/r/p-autocomplete-10.json) +- Autocomplete with limited number of results | [JSON](https://coss.com/ui/r/p-autocomplete-11.json) +- Autocomplete with async items loading | [JSON](https://coss.com/ui/r/p-autocomplete-12.json) +- Autocomplete form | [JSON](https://coss.com/ui/r/p-autocomplete-13.json) +- Autocomplete form | [JSON](https://coss.com/ui/r/p-autocomplete-14.json) +- Pill-shaped autocomplete | [JSON](https://coss.com/ui/r/p-autocomplete-15.json) + +### avatar + +- Avatar with image and fallback | [JSON](https://coss.com/ui/r/p-avatar-1.json) +- Fallback-only avatar | [JSON](https://coss.com/ui/r/p-avatar-2.json) +- Avatars with different sizes | [JSON](https://coss.com/ui/r/p-avatar-3.json) +- Avatars with different radii | [JSON](https://coss.com/ui/r/p-avatar-4.json) +- Overlapping avatar group | [JSON](https://coss.com/ui/r/p-avatar-5.json) +- Avatar with user icon fallback | [JSON](https://coss.com/ui/r/p-avatar-6.json) +- Avatar with emerald status dot | [JSON](https://coss.com/ui/r/p-avatar-7.json) +- Avatar with muted status dot | [JSON](https://coss.com/ui/r/p-avatar-8.json) +- Rounded avatar with top-right emerald status | [JSON](https://coss.com/ui/r/p-avatar-9.json) +- Avatar with notification badge | [JSON](https://coss.com/ui/r/p-avatar-10.json) +- Rounded avatar with notification badge | [JSON](https://coss.com/ui/r/p-avatar-11.json) +- Avatar with verified badge | [JSON](https://coss.com/ui/r/p-avatar-12.json) +- Small overlapping avatar group | [JSON](https://coss.com/ui/r/p-avatar-13.json) +- Large overlapping avatar group | [JSON](https://coss.com/ui/r/p-avatar-14.json) + +### badge + +- Basic badge | [JSON](https://coss.com/ui/r/p-badge-1.json) +- Outline badge | [JSON](https://coss.com/ui/r/p-badge-2.json) +- Secondary badge | [JSON](https://coss.com/ui/r/p-badge-3.json) +- Destructive badge | [JSON](https://coss.com/ui/r/p-badge-4.json) +- Info badge | [JSON](https://coss.com/ui/r/p-badge-5.json) +- Success badge | [JSON](https://coss.com/ui/r/p-badge-6.json) +- Warning badge | [JSON](https://coss.com/ui/r/p-badge-7.json) +- Error badge | [JSON](https://coss.com/ui/r/p-badge-8.json) +- Small badge | [JSON](https://coss.com/ui/r/p-badge-9.json) +- Large badge | [JSON](https://coss.com/ui/r/p-badge-10.json) +- Badge with icon | [JSON](https://coss.com/ui/r/p-badge-11.json) +- Badge with link | [JSON](https://coss.com/ui/r/p-badge-12.json) +- Badge with count | [JSON](https://coss.com/ui/r/p-badge-13.json) +- Full rounded badge (pill) | [JSON](https://coss.com/ui/r/p-badge-14.json) +- Badge with number after text | [JSON](https://coss.com/ui/r/p-badge-15.json) +- Status badge - Paid | [JSON](https://coss.com/ui/r/p-badge-16.json) +- Status badge - Pending | [JSON](https://coss.com/ui/r/p-badge-17.json) +- Status badge - Failed | [JSON](https://coss.com/ui/r/p-badge-18.json) +- Selectable badge with checkbox | [JSON](https://coss.com/ui/r/p-badge-19.json) +- Removable badge | [JSON](https://coss.com/ui/r/p-badge-20.json) + +### breadcrumb + +- Breadcrumb with menu example | [JSON](https://coss.com/ui/r/p-breadcrumb-1.json) +- Breadcrumb with custom separator | [JSON](https://coss.com/ui/r/p-breadcrumb-2.json) +- Breadcrumb with home icon for home link only | [JSON](https://coss.com/ui/r/p-breadcrumb-3.json) +- Breadcrumb with folders icon menu | [JSON](https://coss.com/ui/r/p-breadcrumb-4.json) +- Breadcrumb with icons before text | [JSON](https://coss.com/ui/r/p-breadcrumb-5.json) +- Breadcrumb with dot separators | [JSON](https://coss.com/ui/r/p-breadcrumb-6.json) +- Breadcrumb with select dropdown | [JSON](https://coss.com/ui/r/p-breadcrumb-7.json) + +### button + +- Default button | [JSON](https://coss.com/ui/r/p-button-1.json) +- Outline button | [JSON](https://coss.com/ui/r/p-button-2.json) +- Secondary button | [JSON](https://coss.com/ui/r/p-button-3.json) +- Destructive button | [JSON](https://coss.com/ui/r/p-button-4.json) +- Destructive outline button | [JSON](https://coss.com/ui/r/p-button-5.json) +- Ghost button | [JSON](https://coss.com/ui/r/p-button-6.json) +- Link button | [JSON](https://coss.com/ui/r/p-button-7.json) +- Extra-small button | [JSON](https://coss.com/ui/r/p-button-8.json) +- Small button | [JSON](https://coss.com/ui/r/p-button-9.json) +- Large button | [JSON](https://coss.com/ui/r/p-button-10.json) +- Extra-large button | [JSON](https://coss.com/ui/r/p-button-11.json) +- Disabled button | [JSON](https://coss.com/ui/r/p-button-12.json) +- Icon button | [JSON](https://coss.com/ui/r/p-button-13.json) +- Small icon button | [JSON](https://coss.com/ui/r/p-button-14.json) +- Large icon button | [JSON](https://coss.com/ui/r/p-button-15.json) +- Button with icon | [JSON](https://coss.com/ui/r/p-button-16.json) +- Link rendered as button | [JSON](https://coss.com/ui/r/p-button-17.json) +- Button using the built-in loading prop | [JSON](https://coss.com/ui/r/p-button-41.json) +- Custom loading button with manual Spinner | [JSON](https://coss.com/ui/r/p-button-18.json) +- Expandable show more/less toggle button | [JSON](https://coss.com/ui/r/p-button-19.json) +- Back link button with chevron | [JSON](https://coss.com/ui/r/p-button-20.json) +- Card-style button with heading and description | [JSON](https://coss.com/ui/r/p-button-21.json) +- Directional pad control buttons | [JSON](https://coss.com/ui/r/p-button-22.json) +- Outline like button with count | [JSON](https://coss.com/ui/r/p-button-23.json) +- Social login icon buttons | [JSON](https://coss.com/ui/r/p-button-24.json) +- Star button with count badge | [JSON](https://coss.com/ui/r/p-button-26.json) +- Button group with QR code icon and sign in | [JSON](https://coss.com/ui/r/p-button-27.json) +- Button with avatar | [JSON](https://coss.com/ui/r/p-button-28.json) +- Pill-shaped button with rounded-full styling | [JSON](https://coss.com/ui/r/p-button-29.json) +- Button with animated arrow on hover | [JSON](https://coss.com/ui/r/p-button-30.json) +- Button with keyboard shortcut indicator | [JSON](https://coss.com/ui/r/p-button-31.json) +- Button with notification badge | [JSON](https://coss.com/ui/r/p-button-32.json) +- Paired buttons (Cancel/Save) | [JSON](https://coss.com/ui/r/p-button-33.json) +- Button with animated status dot | [JSON](https://coss.com/ui/r/p-button-34.json) +- Icon-only copy button with feedback | [JSON](https://coss.com/ui/r/p-button-35.json) +- Copy button with feedback | [JSON](https://coss.com/ui/r/p-button-36.json) +- Rotating icon button (FAB-style toggle) | [JSON](https://coss.com/ui/r/p-button-37.json) +- Hamburger menu button with animated icon | [JSON](https://coss.com/ui/r/p-button-39.json) +- Download button with progress and cancel action | [JSON](https://coss.com/ui/r/p-button-40.json) +- Social login buttons (Google, X, GitHub) | [JSON](https://coss.com/ui/r/p-button-38.json) + +### calendar + +- Basic calendar | [JSON](https://coss.com/ui/r/p-calendar-1.json) +- Calendar with date range selection | [JSON](https://coss.com/ui/r/p-calendar-3.json) +- Calendar with month/year dropdown navigation | [JSON](https://coss.com/ui/r/p-calendar-4.json) +- Calendar with custom Select dropdown for month/year | [JSON](https://coss.com/ui/r/p-calendar-5.json) +- Calendar with Combobox dropdown for month/year | [JSON](https://coss.com/ui/r/p-calendar-6.json) +- Calendar with disabled dates | [JSON](https://coss.com/ui/r/p-calendar-7.json) +- Calendar with multiple date selection | [JSON](https://coss.com/ui/r/p-calendar-8.json) +- Calendar with custom cell size | [JSON](https://coss.com/ui/r/p-calendar-2.json) +- Calendar with rounded day buttons | [JSON](https://coss.com/ui/r/p-calendar-9.json) +- Calendar with rounded range selection style | [JSON](https://coss.com/ui/r/p-calendar-10.json) +- Calendar with right-aligned navigation | [JSON](https://coss.com/ui/r/p-calendar-11.json) +- Calendar with week numbers | [JSON](https://coss.com/ui/r/p-calendar-12.json) +- Calendar with year-only combobox dropdown | [JSON](https://coss.com/ui/r/p-calendar-13.json) +- Calendar without arrow navigation (dropdown only) | [JSON](https://coss.com/ui/r/p-calendar-14.json) +- Calendar with current month button | [JSON](https://coss.com/ui/r/p-calendar-15.json) +- Calendar with today button | [JSON](https://coss.com/ui/r/p-calendar-16.json) +- Calendar with date input | [JSON](https://coss.com/ui/r/p-calendar-17.json) +- Calendar with time input | [JSON](https://coss.com/ui/r/p-calendar-18.json) +- Calendar with time slots (appointment picker) | [JSON](https://coss.com/ui/r/p-calendar-19.json) +- Calendar with date presets | [JSON](https://coss.com/ui/r/p-calendar-20.json) +- Range calendar with date presets | [JSON](https://coss.com/ui/r/p-calendar-21.json) +- Two months calendar | [JSON](https://coss.com/ui/r/p-calendar-22.json) +- Three months calendar | [JSON](https://coss.com/ui/r/p-calendar-23.json) +- Pricing calendar with custom day buttons | [JSON](https://coss.com/ui/r/p-calendar-24.json) + +### card + +- A basic card with header and footer | [JSON](https://coss.com/ui/r/p-card-1.json) +- Authentication card with actions | [JSON](https://coss.com/ui/r/p-card-2.json) +- Authentication card with separators | [JSON](https://coss.com/ui/r/p-card-3.json) +- Framed card with footer | [JSON](https://coss.com/ui/r/p-card-4.json) +- Framed card with header | [JSON](https://coss.com/ui/r/p-card-5.json) +- Framed card with header and footer | [JSON](https://coss.com/ui/r/p-card-6.json) +- Framed card with no rounded bottom | [JSON](https://coss.com/ui/r/p-card-7.json) +- Card within a frame and footer | [JSON](https://coss.com/ui/r/p-card-8.json) +- Card within a frame and footer | [JSON](https://coss.com/ui/r/p-card-9.json) +- Card within a frame with header and footer | [JSON](https://coss.com/ui/r/p-card-10.json) +- CardFrame with header action | [JSON](https://coss.com/ui/r/p-card-11.json) + +### checkbox + +- Basic checkbox | [JSON](https://coss.com/ui/r/p-checkbox-1.json) +- Disabled checkbox | [JSON](https://coss.com/ui/r/p-checkbox-2.json) +- Checkbox with description | [JSON](https://coss.com/ui/r/p-checkbox-3.json) +- Card-style checkbox | [JSON](https://coss.com/ui/r/p-checkbox-4.json) +- Checkbox form | [JSON](https://coss.com/ui/r/p-checkbox-5.json) + +### checkbox-group + +- Basic checkbox group | [JSON](https://coss.com/ui/r/p-checkbox-group-1.json) +- Checkbox group with disabled items | [JSON](https://coss.com/ui/r/p-checkbox-group-2.json) +- Checkbox group with parent checkbox | [JSON](https://coss.com/ui/r/p-checkbox-group-3.json) +- Nested checkbox group with parent | [JSON](https://coss.com/ui/r/p-checkbox-group-4.json) +- Checkbox group form | [JSON](https://coss.com/ui/r/p-checkbox-group-5.json) + +### collapsible + +- Basic collapsible | [JSON](https://coss.com/ui/r/p-collapsible-1.json) + +### combobox + +- Basic combobox | [JSON](https://coss.com/ui/r/p-combobox-1.json) +- Disabled combobox | [JSON](https://coss.com/ui/r/p-combobox-2.json) +- Small combobox | [JSON](https://coss.com/ui/r/p-combobox-3.json) +- Large combobox | [JSON](https://coss.com/ui/r/p-combobox-4.json) +- Combobox with label | [JSON](https://coss.com/ui/r/p-combobox-5.json) +- Combobox auto highlighting the first option | [JSON](https://coss.com/ui/r/p-combobox-6.json) +- Combobox with clear button | [JSON](https://coss.com/ui/r/p-combobox-7.json) +- Combobox with grouped items | [JSON](https://coss.com/ui/r/p-combobox-8.json) +- Combobox with multiple selection | [JSON](https://coss.com/ui/r/p-combobox-9.json) +- Combobox with input inside popup | [JSON](https://coss.com/ui/r/p-combobox-10.json) +- Combobox form | [JSON](https://coss.com/ui/r/p-combobox-11.json) +- Combobox multiple form | [JSON](https://coss.com/ui/r/p-combobox-12.json) +- Combobox with start addon | [JSON](https://coss.com/ui/r/p-combobox-13.json) +- Combobox multiple with start addon | [JSON](https://coss.com/ui/r/p-combobox-14.json) +- Pill-shaped combobox | [JSON](https://coss.com/ui/r/p-combobox-15.json) +- Timezone combobox | [JSON](https://coss.com/ui/r/p-combobox-16.json) +- Timezone combobox with search input | [JSON](https://coss.com/ui/r/p-combobox-17.json) +- Combobox with select trigger | [JSON](https://coss.com/ui/r/p-combobox-18.json) + +### command + +- Command palette with dialog | [JSON](https://coss.com/ui/r/p-command-1.json) +- Command palette with AI assistant | [JSON](https://coss.com/ui/r/p-command-2.json) + +### date-picker + +- Basic date picker | [JSON](https://coss.com/ui/r/p-date-picker-1.json) +- Date range picker | [JSON](https://coss.com/ui/r/p-date-picker-2.json) +- Two months calendar with range date | [JSON](https://coss.com/ui/r/p-date-picker-9.json) +- Date picker with field and dropdown navigation | [JSON](https://coss.com/ui/r/p-date-picker-3.json) +- Date picker with presets | [JSON](https://coss.com/ui/r/p-date-picker-4.json) +- Date picker with input | [JSON](https://coss.com/ui/r/p-date-picker-5.json) +- Date picker that closes on select | [JSON](https://coss.com/ui/r/p-date-picker-6.json) +- Multiple dates picker | [JSON](https://coss.com/ui/r/p-date-picker-7.json) +- Date picker with select-like trigger | [JSON](https://coss.com/ui/r/p-date-picker-8.json) + +### dialog + +- Dialog with form | [JSON](https://coss.com/ui/r/p-dialog-1.json) +- Dialog with bare footer | [JSON](https://coss.com/ui/r/p-dialog-6.json) +- Dialog opened from menu | [JSON](https://coss.com/ui/r/p-dialog-2.json) +- Nested dialogs | [JSON](https://coss.com/ui/r/p-dialog-3.json) +- Dialog with close confirmation | [JSON](https://coss.com/ui/r/p-dialog-4.json) +- Dialog with long content | [JSON](https://coss.com/ui/r/p-dialog-5.json) + +### drawer + +- Simple bottom drawer with close button | [JSON](https://coss.com/ui/r/p-drawer-1.json) +- Bottom drawer without drag bar | [JSON](https://coss.com/ui/r/p-drawer-2.json) +- Drawer with close button | [JSON](https://coss.com/ui/r/p-drawer-3.json) +- Inset variant drawers for all four positions | [JSON](https://coss.com/ui/r/p-drawer-4.json) +- Straight variant drawers for all four positions | [JSON](https://coss.com/ui/r/p-drawer-5.json) +- Scrollable content with terms and conditions | [JSON](https://coss.com/ui/r/p-drawer-6.json) +- Nested bottom drawers with centered content | [JSON](https://coss.com/ui/r/p-drawer-7.json) +- Nested right drawers with inset variant | [JSON](https://coss.com/ui/r/p-drawer-8.json) +- Bottom drawer with snap points | [JSON](https://coss.com/ui/r/p-drawer-9.json) +- Edit profile form with default and bare footer variants | [JSON](https://coss.com/ui/r/p-drawer-10.json) +- Mobile menu drawer from the left | [JSON](https://coss.com/ui/r/p-drawer-11.json) +- Responsive edit profile: dialog on desktop, drawer on mobile | [JSON](https://coss.com/ui/r/p-drawer-12.json) +- Responsive actions menu: menu on desktop, drawer on mobile | [JSON](https://coss.com/ui/r/p-drawer-13.json) +- Left drawer with swipe area | [JSON](https://coss.com/ui/r/p-drawer-14.json) + +### empty + +- Empty state with icon and actions | [JSON](https://coss.com/ui/r/p-empty-1.json) + +### field + +- Field with description | [JSON](https://coss.com/ui/r/p-field-1.json) +- Field with required indicator | [JSON](https://coss.com/ui/r/p-field-2.json) +- Field in disabled state | [JSON](https://coss.com/ui/r/p-field-3.json) +- Field showing validation error | [JSON](https://coss.com/ui/r/p-field-4.json) +- Show field validity state | [JSON](https://coss.com/ui/r/p-field-5.json) +- Input group with field | [JSON](https://coss.com/ui/r/p-field-6.json) +- Field with autocomplete | [JSON](https://coss.com/ui/r/p-field-7.json) +- Field with combobox | [JSON](https://coss.com/ui/r/p-field-8.json) +- Field with multiple selection combobox | [JSON](https://coss.com/ui/r/p-field-9.json) +- Field with textarea | [JSON](https://coss.com/ui/r/p-field-10.json) +- Field with select | [JSON](https://coss.com/ui/r/p-field-11.json) +- Field with checkbox | [JSON](https://coss.com/ui/r/p-field-12.json) +- Field with checkbox group | [JSON](https://coss.com/ui/r/p-field-13.json) +- Field with radio group | [JSON](https://coss.com/ui/r/p-field-14.json) +- Field with toggle switch | [JSON](https://coss.com/ui/r/p-field-15.json) +- Field with slider | [JSON](https://coss.com/ui/r/p-field-16.json) +- Field with number field | [JSON](https://coss.com/ui/r/p-field-17.json) +- Complete form built with field | [JSON](https://coss.com/ui/r/p-field-18.json) + +### fieldset + +- Fieldset with multiple fields | [JSON](https://coss.com/ui/r/p-fieldset-1.json) + +### form + +- Input in a form | [JSON](https://coss.com/ui/r/p-form-1.json) +- Form with zod validation | [JSON](https://coss.com/ui/r/p-form-2.json) + +### frame + +- Basic frame | [JSON](https://coss.com/ui/r/p-frame-1.json) +- Frame with multiple separated panels | [JSON](https://coss.com/ui/r/p-frame-3.json) +- Frame with multiple stacked panels | [JSON](https://coss.com/ui/r/p-frame-4.json) +- Frame with collapsible content and delete button | [JSON](https://coss.com/ui/r/p-frame-2.json) + +### group + +- Basic group | [JSON](https://coss.com/ui/r/p-group-1.json) +- Group with input | [JSON](https://coss.com/ui/r/p-group-2.json) +- Small group | [JSON](https://coss.com/ui/r/p-group-3.json) +- Large group | [JSON](https://coss.com/ui/r/p-group-4.json) +- Group with disabled button | [JSON](https://coss.com/ui/r/p-group-5.json) +- Group with default button | [JSON](https://coss.com/ui/r/p-group-6.json) +- Group with start text | [JSON](https://coss.com/ui/r/p-group-7.json) +- Group with end text | [JSON](https://coss.com/ui/r/p-group-8.json) +- Vertical group | [JSON](https://coss.com/ui/r/p-group-9.json) +- Nested groups | [JSON](https://coss.com/ui/r/p-group-10.json) +- Group with popup | [JSON](https://coss.com/ui/r/p-group-11.json) +- Group with input group | [JSON](https://coss.com/ui/r/p-group-12.json) +- Group with menu | [JSON](https://coss.com/ui/r/p-group-13.json) +- Group with select | [JSON](https://coss.com/ui/r/p-group-14.json) +- Group with search | [JSON](https://coss.com/ui/r/p-group-15.json) +- Group with add button and input | [JSON](https://coss.com/ui/r/p-group-16.json) +- Group with input and currency text | [JSON](https://coss.com/ui/r/p-group-17.json) +- Group with select and input | [JSON](https://coss.com/ui/r/p-group-18.json) +- Group with input and select | [JSON](https://coss.com/ui/r/p-group-19.json) +- Group with input and text button | [JSON](https://coss.com/ui/r/p-group-20.json) +- Group with two number inputs for range | [JSON](https://coss.com/ui/r/p-group-22.json) +- Group with filter label, combobox multi-select, and remove button | [JSON](https://coss.com/ui/r/p-group-23.json) + +### input + +- Basic input | [JSON](https://coss.com/ui/r/p-input-1.json) +- Small input | [JSON](https://coss.com/ui/r/p-input-2.json) +- Large input | [JSON](https://coss.com/ui/r/p-input-3.json) +- Disabled input | [JSON](https://coss.com/ui/r/p-input-4.json) +- File input | [JSON](https://coss.com/ui/r/p-input-5.json) +- Input with label | [JSON](https://coss.com/ui/r/p-input-6.json) +- Input with button using Group | [JSON](https://coss.com/ui/r/p-input-7.json) +- Input with start text and end tooltip | [JSON](https://coss.com/ui/r/p-input-8.json) +- Password input with toggle visibility | [JSON](https://coss.com/ui/r/p-input-9.json) +- Input group mimicking a URL bar | [JSON](https://coss.com/ui/r/p-input-10.json) +- Input group with keyboard shortcut | [JSON](https://coss.com/ui/r/p-input-11.json) +- Input group with start loading spinner | [JSON](https://coss.com/ui/r/p-input-12.json) +- Input with label and required indicator | [JSON](https://coss.com/ui/r/p-input-13.json) +- Input with optional label | [JSON](https://coss.com/ui/r/p-input-14.json) +- Input with custom border and background | [JSON](https://coss.com/ui/r/p-input-15.json) +- Input group with end loading spinner | [JSON](https://coss.com/ui/r/p-input-16.json) +- Read-only input | [JSON](https://coss.com/ui/r/p-input-17.json) +- Input with characters remaining counter | [JSON](https://coss.com/ui/r/p-input-18.json) +- Pill-shaped input | [JSON](https://coss.com/ui/r/p-input-19.json) + +### input-group + +- Basic input group | [JSON](https://coss.com/ui/r/p-input-group-1.json) +- Input group with end icon | [JSON](https://coss.com/ui/r/p-input-group-2.json) +- Input group with start text | [JSON](https://coss.com/ui/r/p-input-group-3.json) +- Input group with end text | [JSON](https://coss.com/ui/r/p-input-group-4.json) +- Input group with start and end text | [JSON](https://coss.com/ui/r/p-input-group-5.json) +- Input group with number field | [JSON](https://coss.com/ui/r/p-input-group-6.json) +- Input group with end tooltip | [JSON](https://coss.com/ui/r/p-input-group-7.json) +- Input group with icon button | [JSON](https://coss.com/ui/r/p-input-group-8.json) +- Input group with button | [JSON](https://coss.com/ui/r/p-input-group-9.json) +- Input group with badge | [JSON](https://coss.com/ui/r/p-input-group-10.json) +- Input group with keyboard shortcut | [JSON](https://coss.com/ui/r/p-input-group-11.json) +- Input group with inner label | [JSON](https://coss.com/ui/r/p-input-group-12.json) +- Small input group | [JSON](https://coss.com/ui/r/p-input-group-13.json) +- Large input group | [JSON](https://coss.com/ui/r/p-input-group-14.json) +- Disabled input group | [JSON](https://coss.com/ui/r/p-input-group-15.json) +- Input group with loading spinner | [JSON](https://coss.com/ui/r/p-input-group-16.json) +- Input group with textarea | [JSON](https://coss.com/ui/r/p-input-group-17.json) +- Input group with badge and menu | [JSON](https://coss.com/ui/r/p-input-group-18.json) +- Mini editor built with input group and toggle | [JSON](https://coss.com/ui/r/p-input-group-19.json) +- Input group with search icon | [JSON](https://coss.com/ui/r/p-input-group-20.json) +- Input group with start tooltip | [JSON](https://coss.com/ui/r/p-input-group-21.json) +- Input group with clear button | [JSON](https://coss.com/ui/r/p-input-group-22.json) +- Search input group with loader and voice button | [JSON](https://coss.com/ui/r/p-input-group-23.json) +- Input group with character counter | [JSON](https://coss.com/ui/r/p-input-group-24.json) +- Password input with strength indicator | [JSON](https://coss.com/ui/r/p-input-group-26.json) +- Code snippet input with language selector | [JSON](https://coss.com/ui/r/p-input-group-27.json) +- Message composer with attachment buttons | [JSON](https://coss.com/ui/r/p-input-group-28.json) +- Chat input with voice and send buttons | [JSON](https://coss.com/ui/r/p-input-group-29.json) + +### kbd + +- Keyboard shortcuts display | [JSON](https://coss.com/ui/r/p-kbd-1.json) + +### menu + +- Basic menu | [JSON](https://coss.com/ui/r/p-menu-1.json) +- Menu with hover | [JSON](https://coss.com/ui/r/p-menu-2.json) +- Menu with checkbox | [JSON](https://coss.com/ui/r/p-menu-3.json) +- Menu with checkbox items as switches | [JSON](https://coss.com/ui/r/p-menu-9.json) +- Menu with radio group | [JSON](https://coss.com/ui/r/p-menu-4.json) +- Menu with link | [JSON](https://coss.com/ui/r/p-menu-5.json) +- Menu with group labels | [JSON](https://coss.com/ui/r/p-menu-6.json) +- Nested menu | [JSON](https://coss.com/ui/r/p-menu-7.json) +- Menu close on click | [JSON](https://coss.com/ui/r/p-menu-8.json) + +### meter + +- Basic meter | [JSON](https://coss.com/ui/r/p-meter-1.json) +- Simple meter | [JSON](https://coss.com/ui/r/p-meter-2.json) +- Meter with formatted value | [JSON](https://coss.com/ui/r/p-meter-3.json) +- Meter with range | [JSON](https://coss.com/ui/r/p-meter-4.json) + +### number-field + +- Basic number field | [JSON](https://coss.com/ui/r/p-number-field-1.json) +- Small number field | [JSON](https://coss.com/ui/r/p-number-field-2.json) +- Large number field | [JSON](https://coss.com/ui/r/p-number-field-3.json) +- Disabled number field | [JSON](https://coss.com/ui/r/p-number-field-4.json) +- Number field with label | [JSON](https://coss.com/ui/r/p-number-field-5.json) +- Number field with scrub | [JSON](https://coss.com/ui/r/p-number-field-6.json) +- Number field with range | [JSON](https://coss.com/ui/r/p-number-field-7.json) +- Number field with formatted value | [JSON](https://coss.com/ui/r/p-number-field-8.json) +- Number field with step | [JSON](https://coss.com/ui/r/p-number-field-9.json) +- Number field in form | [JSON](https://coss.com/ui/r/p-number-field-10.json) +- Pill-shaped number field | [JSON](https://coss.com/ui/r/p-number-field-11.json) + +### otp-field + +- Basic OTP field | [JSON](https://coss.com/ui/r/p-otp-field-1.json) +- Large OTP field | [JSON](https://coss.com/ui/r/p-otp-field-2.json) +- OTP field with separator | [JSON](https://coss.com/ui/r/p-otp-field-3.json) +- OTP field with label | [JSON](https://coss.com/ui/r/p-otp-field-4.json) +- OTP field with custom sanitization | [JSON](https://coss.com/ui/r/p-otp-field-6.json) +- OTP field with auto validation | [JSON](https://coss.com/ui/r/p-otp-field-7.json) +- Alphanumeric OTP field | [JSON](https://coss.com/ui/r/p-otp-field-8.json) +- OTP field with placeholder hints | [JSON](https://coss.com/ui/r/p-otp-field-9.json) +- Masked OTP field | [JSON](https://coss.com/ui/r/p-otp-field-10.json) + +### pagination + +- Pagination example | [JSON](https://coss.com/ui/r/p-pagination-1.json) +- Pagination with previous and next buttons only | [JSON](https://coss.com/ui/r/p-pagination-2.json) +- Pagination with select, and previous and next buttons | [JSON](https://coss.com/ui/r/p-pagination-3.json) + +### popover + +- Popover with a form | [JSON](https://coss.com/ui/r/p-popover-1.json) +- Popover with close button | [JSON](https://coss.com/ui/r/p-popover-2.json) +- Animated popovers | [JSON](https://coss.com/ui/r/p-popover-3.json) + +### preview-card + +- Preview card with popup | [JSON](https://coss.com/ui/r/p-preview-card-1.json) + +### progress + +- Basic progress bar | [JSON](https://coss.com/ui/r/p-progress-1.json) +- Progress with label and value | [JSON](https://coss.com/ui/r/p-progress-2.json) +- Progress with formatted value | [JSON](https://coss.com/ui/r/p-progress-3.json) + +### radio-group + +- Basic radio group | [JSON](https://coss.com/ui/r/p-radio-group-1.json) +- Disabled radio group | [JSON](https://coss.com/ui/r/p-radio-group-2.json) +- Radio group with description | [JSON](https://coss.com/ui/r/p-radio-group-3.json) +- Radio group card | [JSON](https://coss.com/ui/r/p-radio-group-4.json) +- Radio group in form | [JSON](https://coss.com/ui/r/p-radio-group-5.json) +- Theme selector with image cards | [JSON](https://coss.com/ui/r/p-radio-group-6.json) + +### scroll-area + +- Basic scroll area | [JSON](https://coss.com/ui/r/p-scroll-area-1.json) +- Horizontal scroll area | [JSON](https://coss.com/ui/r/p-scroll-area-2.json) +- Scroll area with both directions | [JSON](https://coss.com/ui/r/p-scroll-area-3.json) +- Scroll area with fading edges | [JSON](https://coss.com/ui/r/p-scroll-area-4.json) +- Horizontal scroll area with scrollbar gutter | [JSON](https://coss.com/ui/r/p-scroll-area-5.json) + +### select + +- Basic select | [JSON](https://coss.com/ui/r/p-select-1.json) +- Small select | [JSON](https://coss.com/ui/r/p-select-2.json) +- Large select | [JSON](https://coss.com/ui/r/p-select-3.json) +- Disabled select | [JSON](https://coss.com/ui/r/p-select-4.json) +- Select without item alignment | [JSON](https://coss.com/ui/r/p-select-5.json) +- Select with groups | [JSON](https://coss.com/ui/r/p-select-6.json) +- Multiple select | [JSON](https://coss.com/ui/r/p-select-7.json) +- Select with icon | [JSON](https://coss.com/ui/r/p-select-8.json) +- Select options with icon | [JSON](https://coss.com/ui/r/p-select-9.json) +- Select with object values | [JSON](https://coss.com/ui/r/p-select-10.json) +- Select with disabled items | [JSON](https://coss.com/ui/r/p-select-12.json) +- Timezone select | [JSON](https://coss.com/ui/r/p-select-13.json) +- Status select with colored dot | [JSON](https://coss.com/ui/r/p-select-14.json) +- Pill-shaped select trigger | [JSON](https://coss.com/ui/r/p-select-15.json) +- Select with left text label | [JSON](https://coss.com/ui/r/p-select-16.json) +- Select with country flags | [JSON](https://coss.com/ui/r/p-select-17.json) +- Select with description in options only | [JSON](https://coss.com/ui/r/p-select-18.json) +- Select with avatars | [JSON](https://coss.com/ui/r/p-select-19.json) +- Rich select with avatars and usernames | [JSON](https://coss.com/ui/r/p-select-20.json) +- Auto width select | [JSON](https://coss.com/ui/r/p-select-21.json) +- Select with custom border and background | [JSON](https://coss.com/ui/r/p-select-22.json) +- Select with label | [JSON](https://coss.com/ui/r/p-select-23.json) +- Select in form | [JSON](https://coss.com/ui/r/p-select-11.json) + +### separator + +- Separator with horizontal and vertical orientations | [JSON](https://coss.com/ui/r/p-separator-1.json) + +### sheet + +- Basic sheet | [JSON](https://coss.com/ui/r/p-sheet-1.json) +- Sheet inset | [JSON](https://coss.com/ui/r/p-sheet-2.json) +- Sheet position | [JSON](https://coss.com/ui/r/p-sheet-3.json) + +### skeleton + +- Basic skeleton | [JSON](https://coss.com/ui/r/p-skeleton-1.json) +- Skeleton only | [JSON](https://coss.com/ui/r/p-skeleton-2.json) + +### slider + +- Basic slider | [JSON](https://coss.com/ui/r/p-slider-1.json) +- Slider with label and value | [JSON](https://coss.com/ui/r/p-slider-2.json) +- Disabled slider | [JSON](https://coss.com/ui/r/p-slider-3.json) +- Slider with reference labels | [JSON](https://coss.com/ui/r/p-slider-4.json) +- Slider with ticks | [JSON](https://coss.com/ui/r/p-slider-5.json) +- Slider with labels above | [JSON](https://coss.com/ui/r/p-slider-6.json) +- Range slider | [JSON](https://coss.com/ui/r/p-slider-7.json) +- Slider with 3 thumbs | [JSON](https://coss.com/ui/r/p-slider-8.json) +- Range slider with collision behavior none | [JSON](https://coss.com/ui/r/p-slider-9.json) +- Range slider with collision behavior swap | [JSON](https://coss.com/ui/r/p-slider-10.json) +- Slider with icons | [JSON](https://coss.com/ui/r/p-slider-11.json) +- Slider with input | [JSON](https://coss.com/ui/r/p-slider-12.json) +- Range slider with inputs | [JSON](https://coss.com/ui/r/p-slider-13.json) +- Slider with increment and decrement buttons | [JSON](https://coss.com/ui/r/p-slider-14.json) +- Price range slider | [JSON](https://coss.com/ui/r/p-slider-15.json) +- Emoji rating slider | [JSON](https://coss.com/ui/r/p-slider-16.json) +- Vertical slider | [JSON](https://coss.com/ui/r/p-slider-17.json) +- Vertical range slider | [JSON](https://coss.com/ui/r/p-slider-18.json) +- Vertical slider with input | [JSON](https://coss.com/ui/r/p-slider-19.json) +- Equalizer with vertical sliders | [JSON](https://coss.com/ui/r/p-slider-20.json) +- Object position sliders with reset | [JSON](https://coss.com/ui/r/p-slider-21.json) +- Price slider with histogram | [JSON](https://coss.com/ui/r/p-slider-22.json) +- Slider in form | [JSON](https://coss.com/ui/r/p-slider-23.json) + +### spinner + +- Basic spinner | [JSON](https://coss.com/ui/r/p-spinner-1.json) + +### switch + +- Basic switch | [JSON](https://coss.com/ui/r/p-switch-1.json) +- Disabled switch | [JSON](https://coss.com/ui/r/p-switch-2.json) +- Switch with description | [JSON](https://coss.com/ui/r/p-switch-3.json) +- Switch card | [JSON](https://coss.com/ui/r/p-switch-4.json) +- Switch in form | [JSON](https://coss.com/ui/r/p-switch-5.json) +- Custom size switch | [JSON](https://coss.com/ui/r/p-switch-6.json) + +### table + +- Basic table | [JSON](https://coss.com/ui/r/p-table-1.json) +- Frame with card-style table | [JSON](https://coss.com/ui/r/p-table-2.json) +- Table with TanStack Table and checkboxes | [JSON](https://coss.com/ui/r/p-table-3.json) +- Table with TanStack Table, sorting, and pagination | [JSON](https://coss.com/ui/r/p-table-4.json) +- Card-style table variant | [JSON](https://coss.com/ui/r/p-table-5.json) +- CardFrame with card-style table | [JSON](https://coss.com/ui/r/p-table-7.json) +- CardFrame with TanStack Table and checkboxes | [JSON](https://coss.com/ui/r/p-table-6.json) +- CardFrame with TanStack Table, sorting, and pagination | [JSON](https://coss.com/ui/r/p-table-8.json) + +### tabs + +- Basic tabs | [JSON](https://coss.com/ui/r/p-tabs-1.json) +- Tabs with underline | [JSON](https://coss.com/ui/r/p-tabs-2.json) +- Vertical tabs | [JSON](https://coss.com/ui/r/p-tabs-3.json) +- Vertical tabs with underline | [JSON](https://coss.com/ui/r/p-tabs-4.json) +- Tabs with full rounded triggers | [JSON](https://coss.com/ui/r/p-tabs-5.json) +- Tabs with icon before name | [JSON](https://coss.com/ui/r/p-tabs-6.json) +- Tabs with icon before name and underline | [JSON](https://coss.com/ui/r/p-tabs-7.json) +- Tabs with icon only | [JSON](https://coss.com/ui/r/p-tabs-8.json) +- Tabs with underline and icon on top | [JSON](https://coss.com/ui/r/p-tabs-9.json) +- Tabs with count badge | [JSON](https://coss.com/ui/r/p-tabs-10.json) +- Vertical tabs with underline and icon before name | [JSON](https://coss.com/ui/r/p-tabs-11.json) +- Tabs with icon only and count badge | [JSON](https://coss.com/ui/r/p-tabs-12.json) +- Tabs with icon only and grouped tooltips | [JSON](https://coss.com/ui/r/p-tabs-13.json) + +### textarea + +- Basic textarea | [JSON](https://coss.com/ui/r/p-textarea-1.json) +- Small textarea | [JSON](https://coss.com/ui/r/p-textarea-2.json) +- Large textarea | [JSON](https://coss.com/ui/r/p-textarea-3.json) +- Disabled textarea | [JSON](https://coss.com/ui/r/p-textarea-4.json) +- Textarea with label | [JSON](https://coss.com/ui/r/p-textarea-5.json) +- Textarea in form | [JSON](https://coss.com/ui/r/p-textarea-6.json) +- Textarea with label and required indicator | [JSON](https://coss.com/ui/r/p-textarea-7.json) +- Textarea with optional label | [JSON](https://coss.com/ui/r/p-textarea-8.json) +- Textarea with custom border and background | [JSON](https://coss.com/ui/r/p-textarea-9.json) +- Read-only textarea | [JSON](https://coss.com/ui/r/p-textarea-10.json) +- Textarea with characters remaining counter | [JSON](https://coss.com/ui/r/p-textarea-11.json) +- Textarea field with required indicator | [JSON](https://coss.com/ui/r/p-textarea-12.json) +- Shorter textarea with fixed height | [JSON](https://coss.com/ui/r/p-textarea-13.json) +- Textarea with button aligned right | [JSON](https://coss.com/ui/r/p-textarea-14.json) +- Textarea with button aligned left | [JSON](https://coss.com/ui/r/p-textarea-15.json) + +### toast + +- Default stacked toast with title and description | [JSON](https://coss.com/ui/r/p-toast-1.json) +- Stacked toasts by semantic type (success, error, info, warning) | [JSON](https://coss.com/ui/r/p-toast-2.json) +- Loading-state stacked toast | [JSON](https://coss.com/ui/r/p-toast-3.json) +- Stacked toast with primary action (undo) | [JSON](https://coss.com/ui/r/p-toast-4.json) +- Promise-based stacked toast | [JSON](https://coss.com/ui/r/p-toast-5.json) +- Stacked toasts with varying content height | [JSON](https://coss.com/ui/r/p-toast-6.json) +- Anchored tooltip-style toast after copy | [JSON](https://coss.com/ui/r/p-toast-7.json) +- Anchored error toast after async failure | [JSON](https://coss.com/ui/r/p-toast-8.json) +- Long-running promise toast with cancel | [JSON](https://coss.com/ui/r/p-toast-9.json) +- Deduplicated success toast | [JSON](https://coss.com/ui/r/p-toast-10.json) +- Deduplicated error toast | [JSON](https://coss.com/ui/r/p-toast-11.json) +- Anchored deduplicated success toast | [JSON](https://coss.com/ui/r/p-toast-12.json) +- Anchored deduplicated error toast | [JSON](https://coss.com/ui/r/p-toast-13.json) + +### toggle + +- Basic toggle | [JSON](https://coss.com/ui/r/p-toggle-1.json) +- Toggle with outline | [JSON](https://coss.com/ui/r/p-toggle-2.json) +- Toggle with icon | [JSON](https://coss.com/ui/r/p-toggle-3.json) +- Small toggle | [JSON](https://coss.com/ui/r/p-toggle-4.json) +- Large toggle | [JSON](https://coss.com/ui/r/p-toggle-5.json) +- Disabled toggle | [JSON](https://coss.com/ui/r/p-toggle-6.json) +- Toggle icon group | [JSON](https://coss.com/ui/r/p-toggle-7.json) +- Bookmark toggle with tooltip and success toast | [JSON](https://coss.com/ui/r/p-toggle-8.json) + +### toggle-group + +- Basic toggle group | [JSON](https://coss.com/ui/r/p-toggle-group-1.json) +- Small toggle group | [JSON](https://coss.com/ui/r/p-toggle-group-2.json) +- Large toggle group | [JSON](https://coss.com/ui/r/p-toggle-group-3.json) +- Toggle group with outline | [JSON](https://coss.com/ui/r/p-toggle-group-4.json) +- Vertical toggle group with outline | [JSON](https://coss.com/ui/r/p-toggle-group-5.json) +- Disabled toggle group | [JSON](https://coss.com/ui/r/p-toggle-group-6.json) +- Toggle group with disabled item | [JSON](https://coss.com/ui/r/p-toggle-group-7.json) +- Multiple selection toggle group | [JSON](https://coss.com/ui/r/p-toggle-group-8.json) +- Toggle group with tooltips | [JSON](https://coss.com/ui/r/p-toggle-group-9.json) + +### toolbar + +- Toolbar with toggles, buttons, and select | [JSON](https://coss.com/ui/r/p-toolbar-1.json) + +### tooltip + +- Basic tooltip | [JSON](https://coss.com/ui/r/p-tooltip-1.json) +- Grouped tooltips | [JSON](https://coss.com/ui/r/p-tooltip-2.json) +- Toggle group animated tooltip | [JSON](https://coss.com/ui/r/p-tooltip-3.json) +- Vertical group with animated tooltip | [JSON](https://coss.com/ui/r/p-tooltip-4.json) diff --git a/.agents/skills/coss/SKILL.md b/.agents/skills/coss/SKILL.md new file mode 100644 index 0000000..93675fd --- /dev/null +++ b/.agents/skills/coss/SKILL.md @@ -0,0 +1,117 @@ +--- +name: coss +description: Helps implement coss UI components correctly. Use when building UIs with coss primitives (buttons, dialogs, selects, forms, menus, tabs, inputs, toasts, etc.), migrating from shadcn/Radix to coss/Base UI, composing trigger-based overlays, or troubleshooting coss component behavior. Covers imports, accessibility, Tailwind styling, and common pitfalls. +compatibility: Requires Tailwind CSS v4 and @base-ui/react. Designed for React projects using the coss component registry. +license: MIT +metadata: + author: cosscom +--- + +# coss ui + +coss ui is a component library built on Base UI with a shadcn-like developer experience plus a large particle catalog. + +## What this skill is for + +Use this skill to: + +- pick the right coss primitive(s) for a UI task +- write correct coss usage code (imports, composition, props) +- avoid common migration mistakes from shadcn/Radix assumptions +- reference particle examples to produce practical, production-like patterns + +## Source of truth + +- coss components docs: `apps/ui/content/docs/components/*.mdx` + - `https://github.com/cosscom/coss/tree/main/apps/ui/content/docs/components` +- coss particle examples: `apps/ui/registry/default/particles/p-*.tsx` + - `https://github.com/cosscom/coss/tree/main/apps/ui/registry/default/particles` +- coss particles catalog: `https://coss.com/ui/particles` +- docs map for agents: `https://coss.com/ui/llms.txt` + +## Out of scope + +- Maintaining coss monorepo internals/build pipelines. +- Editing registry internals unless explicitly requested. + +## Principles for agent output + +1. Use existing primitives and particles first before inventing custom markup. +2. Prefer composition over custom behavior reimplementation. +3. Follow coss naming and APIs from docs exactly. +4. Keep examples accessible and production-realistic. +5. Prefer concise code that mirrors coss docs/particles conventions. +6. Assume Tailwind CSS v4 conventions in coss examples and setup guidance. + +## Critical usage rules + +Always apply before returning coss code: + +- Do not invent coss APIs. Verify against component docs first. +- For trigger-based primitives (Dialog, Menu, Select, Popover, Tooltip), follow each primitive's documented trigger/content hierarchy and composition API; do not mix patterns across components. +- Preserve accessibility labels and error semantics. +- Consult primitive-specific guides for component invariants and edge cases. +- For manual install guidance, include all required dependencies and local component files referenced by imports. +- Prefer styled coss exports first; use `*Primitive` exports only when custom composition/styling requires it. + +Rule references (read on demand when the task touches these areas): + +- `./references/rules/styling.md` — Tailwind tokens, icon conventions, data-slot selectors +- `./references/rules/forms.md` — Field composition, validation, input patterns +- `./references/rules/composition.md` — Trigger/popup hierarchies, grouped controls +- `./references/rules/migration.md` — shadcn/Radix to coss/Base UI migration patterns +- `./references/portal-props.md` — optional `portalProps` on composed popups and toast providers (`keepMounted`, `container`, which surfaces support it) + +## Component discovery + +All 53 primitives have dedicated reference guides at `./references/primitives/.md`. To find the right one for a task, consult the component registry index: + +- `./references/component-registry.md` + +## Usage workflow + +1. Identify user intent (single primitive, composed flow, form flow, overlay flow, feedback flow). +2. Consult `references/component-registry.md` to identify candidate primitives. +3. Select primitives from coss docs first; avoid custom fallback unless needed. +4. Check at least one particle example for practical composition patterns. Particle files live at `apps/ui/registry/default/particles/p--.tsx` (e.g. `p-dialog-1.tsx`). +5. Write minimal code using documented imports/props. +6. Self-check accessibility and composition invariants. + +## Installation reference + +See `./references/cli.md` for full install/discovery workflow. + +Quick CLI pattern: + +```bash +npx shadcn@latest add @coss/ +``` + +Quick manual pattern: + +- install dependencies listed in the component docs page +- copy required component file(s) +- update imports to match the target app alias setup + +## Primitive Guidance + +Every primitive has a reference guide at `./references/primitives/.md` with imports, minimal patterns, inline code examples, pitfalls, and particle references. Use the component registry to find the right file. + +High-risk primitives (read these guides first -- they have the most composition gotchas): + +- `./references/primitives/dialog.md` — modal overlays, form-in-dialog, responsive dialog/drawer +- `./references/primitives/menu.md` — dropdown actions, checkbox/radio items, submenus +- `./references/primitives/select.md` — items-first pattern, multiple, object values, groups +- `./references/primitives/form.md` — Field composition, validation, submission +- `./references/primitives/input-group.md` — addons, DOM order invariant, textarea layouts +- `./references/primitives/toast.md` — toastManager (not Sonner), anchored toasts, providers + +## Output Checklist + +Before returning code: + +- imports and props match coss docs +- composition structure is valid for selected primitive(s) +- accessibility and explicit control types (`button`, `input`, etc.) are present +- migration-sensitive flows are verified (type/lint, keyboard/a11y behavior, and SSR-sensitive primitives like Select/Command) + diff --git a/.agents/skills/coss/references/cli.md b/.agents/skills/coss/references/cli.md new file mode 100644 index 0000000..cc52100 --- /dev/null +++ b/.agents/skills/coss/references/cli.md @@ -0,0 +1,110 @@ +# coss CLI Reference (Focused) + +Use this guide when installing, previewing, or discovering coss components via the shadcn CLI. + +## CLI Safety Rules + +- Always use the project's package runner: + - `npx shadcn@latest ...` + - `pnpm dlx shadcn@latest ...` + - `bunx --bun shadcn@latest ...` +- Do not invent flags. Use only documented CLI flags. + +## Core Commands for coss Usage + +### Recommended bootstrap paths + +```bash +# New projects (recommended — includes Inter + Geist Mono fonts + full theme) +npx shadcn@latest init @coss/style + +# Existing projects - all primitives +npx shadcn@latest add @coss/ui + +# Existing projects - full theme setup +npx shadcn@latest add @coss/style + +# Existing projects - primitives + color tokens +npx shadcn@latest add @coss/ui @coss/colors-neutral +``` + +`@coss/style` automatically installs `@coss/fonts` (Inter for `--font-sans` and `--font-heading`, Geist Mono for `--font-mono`), which configures all three font variables in `layout.tsx`. No manual font wiring needed. + +### `add` (primary) + +```bash +shadcn add @coss/ +``` + +Examples: + +```bash +npx shadcn@latest add @coss/dialog +npx shadcn@latest add @coss/select +npx shadcn@latest add @coss/toast +``` + +### `add` preview mode (recommended) + +```bash +npx shadcn@latest add @coss/dialog --dry-run +npx shadcn@latest add @coss/dialog --diff +npx shadcn@latest add @coss/dialog --view +``` + +Use preview mode when: + +- user asks what will change +- component might already exist locally +- you need to inspect output before writing files + +### Optional discovery helpers (use when available) + +```bash +npx shadcn@latest search @coss -q "dialog" +npx shadcn@latest view @coss/dialog +npx shadcn@latest docs dialog +npx shadcn@latest info --json +``` + +If these are unsupported in the environment, use fallback sources below. + +## Discovery Fallback Matrix + +### Inside the coss repo (preferred) + +- `apps/ui/registry/registry-particles.ts` + - `https://github.com/cosscom/coss/blob/main/apps/ui/registry/registry-particles.ts` +- `apps/ui/registry.json` + - `https://github.com/cosscom/coss/blob/main/apps/ui/registry.json` +- `apps/ui/content/docs/components/*.mdx` + - `https://github.com/cosscom/coss/tree/main/apps/ui/content/docs/components` + +### Outside the coss repo + +- coss particles catalog: `https://coss.com/ui/particles` +- coss docs catalog: `https://coss.com/ui/` + +## Manual Install Path + +When users explicitly request manual setup: + +1. Read the target component docs. +2. Install exactly the listed dependencies. +3. Copy all required files (including transitive local imports). +4. Adjust imports for target app aliases. +5. Validate the snippet against docs/particles patterns. + +Important: + +- CLI setup usually wires required theme tokens automatically. +- Manual setup must include required additional tokens (`destructive-foreground`, `info`, `success`, `warning` families) from coss styling docs when relevant. + +## Quick Output Checklist + +Before returning CLI guidance: + +1. runner and command are valid for the user's package manager +2. flags are documented and intentional +3. fallback source is provided if CLI discovery commands are unavailable +4. resulting usage guidance matches coss docs and particles patterns diff --git a/.agents/skills/coss/references/component-registry.md b/.agents/skills/coss/references/component-registry.md new file mode 100644 index 0000000..035afa0 --- /dev/null +++ b/.agents/skills/coss/references/component-registry.md @@ -0,0 +1,74 @@ +# coss Component Registry Index + +Use this file to quickly identify the right coss primitive for a UI task. Each entry includes the component name, a one-line purpose, and the path to its reference guide (relative to the skill root `apps/ui/skills/coss/`). + +For optional **`portalProps`** on composed `*Popup` components and **toast** providers (Base UI portal forwarding), see `./references/portal-props.md`. + +## Overlays & Popups +- **Dialog** — Centered modal requiring user focus. `./references/primitives/dialog.md` +- **AlertDialog** — Destructive/critical confirmation modal. `./references/primitives/alert-dialog.md` +- **Sheet** — Side-panel overlay for settings/details. `./references/primitives/sheet.md` +- **Drawer** — Bottom/side drawer, often mobile-responsive. `./references/primitives/drawer.md` +- **Popover** — Anchored non-modal floating content. `./references/primitives/popover.md` +- **Tooltip** — Short hover/focus hint text. `./references/primitives/tooltip.md` +- **PreviewCard** — Hover-triggered rich entity preview. `./references/primitives/preview-card.md` +- **Menu** — Dropdown action list with groups/submenus. `./references/primitives/menu.md` +- **Command** — Searchable command palette (not cmdk). `./references/primitives/command.md` + +## Selection & Input +- **Select** — Single-choice from predefined list (no search). `./references/primitives/select.md` +- **Combobox** — Searchable selection with filtering. `./references/primitives/combobox.md` +- **Autocomplete** — Free-text with suggestions. `./references/primitives/autocomplete.md` +- **Input** — Single-line text entry. `./references/primitives/input.md` +- **Textarea** — Multi-line text entry. `./references/primitives/textarea.md` +- **InputGroup** — Input with addons (icons, buttons, badges). `./references/primitives/input-group.md` +- **OTPField** — One-time passcode segmented slots. `./references/primitives/otp-field.md` +- **NumberField** — Numeric entry with stepper controls. `./references/primitives/number-field.md` +- **Slider** — Continuous/ranged numeric control. `./references/primitives/slider.md` +- **Calendar** — Date picker / calendar views. `./references/primitives/calendar.md` + +## Forms & Validation +- **Form** — Form validation/submission with Zod. `./references/primitives/form.md` +- **Field** — Label + description + error wiring. `./references/primitives/field.md` +- **Fieldset** — Grouped form controls with legend. `./references/primitives/fieldset.md` +- **Label** — Accessible label for controls. `./references/primitives/label.md` + +## Toggle & Choice +- **Checkbox** — Single boolean toggle. `./references/primitives/checkbox.md` +- **CheckboxGroup** — Multiple-selection set. `./references/primitives/checkbox-group.md` +- **RadioGroup** — Mutually exclusive single choice. `./references/primitives/radio-group.md` +- **Switch** — Binary on/off preference toggle. `./references/primitives/switch.md` +- **Toggle** — Pressable two-state command button. `./references/primitives/toggle.md` +- **ToggleGroup** — Grouped pressed-state controls. `./references/primitives/toggle-group.md` + +## Layout & Navigation +- **Tabs** — Mutually exclusive tabbed panels. `./references/primitives/tabs.md` +- **Accordion** — Collapsible content sections. `./references/primitives/accordion.md` +- **Collapsible** — Single expand/collapse region. `./references/primitives/collapsible.md` +- **Sidebar** — Persistent app shell navigation. `./references/primitives/sidebar.md` +- **Breadcrumb** — Hierarchical navigation trail. `./references/primitives/breadcrumb.md` +- **Pagination** — Paged navigation controls. `./references/primitives/pagination.md` +- **Toolbar** — Grouped command/action strip. `./references/primitives/toolbar.md` +- **ScrollArea** — Styled scroll container. `./references/primitives/scroll-area.md` + +## Content & Display +- **Card** — Content container with sections. `./references/primitives/card.md` +- **Frame** — Bordered content surface. `./references/primitives/frame.md` +- **Table** — Tabular data presentation. `./references/primitives/table.md` +- **Avatar** — User/entity profile image. `./references/primitives/avatar.md` +- **Badge** — Status indicator / label. `./references/primitives/badge.md` +- **Kbd** — Keyboard shortcut hints. `./references/primitives/kbd.md` +- **Separator** — Visual/semantic divider. `./references/primitives/separator.md` +- **Group** — Connected control cluster. `./references/primitives/group.md` +- **Empty** — Empty-state placeholder. `./references/primitives/empty.md` + +## Feedback & Status +- **Alert** — Inline persistent status message. `./references/primitives/alert.md` +- **Toast** — Transient notification (toastManager). `./references/primitives/toast.md` +- **Progress** — Task completion / async progress bar. `./references/primitives/progress.md` +- **Meter** — Bounded scalar measurement. `./references/primitives/meter.md` +- **Spinner** — Indeterminate loading indicator. `./references/primitives/spinner.md` +- **Skeleton** — Loading placeholder. `./references/primitives/skeleton.md` + +## Actions +- **Button** — Primary/secondary action trigger. `./references/primitives/button.md` diff --git a/.agents/skills/coss/references/portal-props.md b/.agents/skills/coss/references/portal-props.md new file mode 100644 index 0000000..5587cd2 --- /dev/null +++ b/.agents/skills/coss/references/portal-props.md @@ -0,0 +1,21 @@ +# Portal forwarding (`portalProps`) + +Several composed coss primitives wrap Base UI **`Portal`** inside `*Popup` (or dialog-style) components, and toast exposes the same for **`ToastProvider`** / **`AnchoredToastProvider`**. Those surfaces accept an optional **`portalProps`** object, which is spread onto the internal `Portal`. + +Use it when you need portal-level behavior from Base UI, for example: + +- **`keepMounted`** — where the component’s `Portal` type supports it (see Base UI typings/docs for that component). +- **`container`** — render portaled content into a specific DOM node (stacking contexts, micro-frontends, shadow DOM setups). +- Other props accepted by that component’s **`Portal.Props`** (including `className` / `ref` when applicable). + +## Surfaces that expose `portalProps` + +- **Modals / overlays:** `DialogPopup`, `AlertDialogPopup`, `SheetPopup`, `DrawerPopup`, `CommandDialogPopup` +- **Floating layers:** `MenuPopup`, `PopoverPopup`, `TooltipPopup`, `PreviewCardPopup`, `AutocompletePopup`, `ComboboxPopup`, `SelectPopup` +- **Toast (providers, not a `*Popup` name):** `ToastProvider`, `AnchoredToastProvider` — `portalProps` is forwarded to the internal `Toast.Portal` for the stacked and anchored viewports, respectively + +Only these surfaces accept `portalProps`. Any other registry component that portals content but is not listed here keeps the portal internal and is out of scope for this prop—compose Base UI parts yourself if you need direct portal control. + +## Positioner vs portal + +`portalProps` only affects the **portal** node. To tweak **placement** or **positioner** styling, use the existing `side`, `align`, `sideOffset`, etc., or compose Base UI **`Positioner`** / pass through future **`positionerProps`** if the wrapper adds them. diff --git a/.agents/skills/coss/references/primitives/accordion.md b/.agents/skills/coss/references/primitives/accordion.md new file mode 100644 index 0000000..8226519 --- /dev/null +++ b/.agents/skills/coss/references/primitives/accordion.md @@ -0,0 +1,92 @@ +# coss Accordion + +## When to use + +- Expandable multi-section content regions. +- FAQs and settings pages with progressive disclosure. + +## Install + +```bash +npx shadcn@latest add @coss/accordion +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Accordion, + AccordionItem, + AccordionPanel, + AccordionTrigger, +} from "@/components/ui/accordion" +``` + +## Minimal pattern + +```tsx + + + What is Base UI? + + Base UI is a library of high-quality unstyled React components. + + + +``` + +## Patterns from coss particles + +### Key patterns + +Multiple panels open simultaneously: + +```tsx + + + Section 1 + Content 1 + + + Section 2 + Content 2 + + +``` + +Controlled mode with external state: + +```tsx +const [value, setValue] = useState(["item-1"]) + + + ... + +``` + +Each `AccordionItem` needs a stable `value`; trigger and panel must be children of the same item. + +### More examples + +See `p-accordion-1` through `p-accordion-4` for mapped items, single-open, multiple-open, and controlled patterns. + +## Common pitfalls + +- Placing `AccordionTrigger`/`AccordionPanel` outside `AccordionItem`. +- Omitting `value` on `AccordionItem`, which breaks item identity and controlled behavior. +- Applying Radix mental models like `type="single" | "multiple"` instead of coss `multiple` + array values. +- Treating controlled `value` as scalar instead of `string[]`. + +## Useful particle references + +- baseline mapped-items accordion: `p-accordion-1` +- single-open static sections: `p-accordion-2` +- multiple-open behavior: `p-accordion-3` +- controlled value + external actions: `p-accordion-4` + diff --git a/.agents/skills/coss/references/primitives/alert-dialog.md b/.agents/skills/coss/references/primitives/alert-dialog.md new file mode 100644 index 0000000..2d09bfe --- /dev/null +++ b/.agents/skills/coss/references/primitives/alert-dialog.md @@ -0,0 +1,85 @@ +# coss Alert Dialog + +## When to use + +- Critical confirmation flows before destructive actions. +- Blocking decisions that require explicit acknowledgement. + +## When NOT to use + +- If the content is informational (no destructive action) -> use Dialog instead. +- If the message is transient feedback -> use Toast instead. +- If the content is contextual and non-blocking -> use Popover instead. + +## Install + +```bash +npx shadcn@latest add @coss/alert-dialog +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + AlertDialog, + AlertDialogClose, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogPopup, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog" +``` + +## Minimal pattern + +```tsx + + }> + Delete Account + + + + Are you absolutely sure? + + This action cannot be undone. + + + + }>Cancel + }> + Delete Account + + + + +``` + +## Patterns from coss particles + +- **Portal forwarding**: optional `portalProps` on `AlertDialogPopup` → Base UI `AlertDialog.Portal` (`keepMounted`, `container`, …). See [portal-props.md](../portal-props.md). +- **Section structure**: keep `AlertDialogHeader` and `AlertDialogFooter` as direct sections of `AlertDialogPopup` (there is no `AlertDialogPanel`; add a `div` or fragment between them only if you need extra body content). +- **Action composition**: use `AlertDialogClose render={ + + + +``` + +### More examples + +See `p-alert-1` through `p-alert-7` for all variants and action patterns. + +## Common pitfalls + +- Using alert variants for passive decoration instead of meaningful semantic status. +- Missing title/description structure in complex alerts, reducing scannability. +- Hiding semantic alert icons with `aria-hidden` when they convey status meaning. + +## Useful particle references + +- with icon: `p-alert-2` +- with icon and action buttons: `p-alert-3` +- info alert: `p-alert-4` +- success alert: `p-alert-5` +- warning alert: `p-alert-6` +- error alert: `p-alert-7` diff --git a/.agents/skills/coss/references/primitives/autocomplete.md b/.agents/skills/coss/references/primitives/autocomplete.md new file mode 100644 index 0000000..d104511 --- /dev/null +++ b/.agents/skills/coss/references/primitives/autocomplete.md @@ -0,0 +1,134 @@ +# coss Autocomplete + +## When to use + +- Search-driven suggestion pickers with free typing. +- Assisted text entry over a known option space with keyboard navigation. + +## When NOT to use + +- If options are predefined and don't need search -> use Select instead. +- If the user must pick from a strict set (no free text) -> use Combobox instead. +- If you need action commands, not data selection -> use Command instead. + +## Install + +```bash +npx shadcn@latest add @coss/autocomplete +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Autocomplete, + AutocompleteCollection, + AutocompleteEmpty, + AutocompleteGroup, + AutocompleteGroupLabel, + AutocompleteInput, + AutocompleteItem, + AutocompleteList, + AutocompletePopup, + AutocompleteSeparator, + AutocompleteStatus, + useAutocompleteFilter, +} from "@/components/ui/autocomplete" +``` + +## Minimal pattern + +```tsx +const items = [ + { label: "Apple", value: "apple" }, + { label: "Banana", value: "banana" }, +] + + + + + No items found. + + {(item) => ( + + {item.label} + + )} + + + +``` + +For form-bound autocomplete controls, prefer `Field` wrappers so label, required state, and error output remain tied to the same control. + +## Patterns from coss particles + +- **Portal forwarding**: optional `portalProps` on `AutocompletePopup` → Base UI combobox/autocomplete portal (`keepMounted`, `container`, …). See [portal-props.md](../portal-props.md). + +### Key patterns + +Autocomplete with input affordances: + +```tsx + + +``` + +Grouped lists: + +```tsx + + + Fruits + + {(item) => {item.label}} + + + +``` + +Async search: use `filter={null}`, control `value`/`onValueChange`, and provide `itemToStringValue` for object results. + +Form integration: place `Autocomplete` inside `Field name="..."` with `FieldLabel`/`FieldError`. + +### More examples + +See `p-autocomplete-1` through `p-autocomplete-15` for sizes, matching behavior, groups, limited results, async, form integration, and pill input patterns. + +## Common pitfalls + +- Omitting `AutocompleteEmpty`, leaving blank popups with no user feedback. +- Using object items in async/custom flows without `itemToStringValue`, which breaks stable string mapping. +- Mixing combobox/select assumptions into autocomplete APIs without checking docs. +- Missing explicit labels (`FieldLabel` or `aria-label`) on the input. +- Not handling async race/error states (`loading`, `error`, stale response cancellation). + +## Useful particle references + +- baseline + size + disabled: `p-autocomplete-1`, `p-autocomplete-2`, `p-autocomplete-3`, `p-autocomplete-4` +- label + input affordances (`showClear`, `showTrigger`, `startAddon`): `p-autocomplete-5`, `p-autocomplete-8`, `p-autocomplete-9`, `p-autocomplete-14` +- matching behavior (`mode="both"`, `autoHighlight`): `p-autocomplete-6`, `p-autocomplete-7` +- grouped options: `p-autocomplete-10` +- limited results with status message: `p-autocomplete-11` +- async search with loading/error status: `p-autocomplete-12` +- form integration: `p-autocomplete-13` +- style variant (pill input): `p-autocomplete-15` diff --git a/.agents/skills/coss/references/primitives/avatar.md b/.agents/skills/coss/references/primitives/avatar.md new file mode 100644 index 0000000..96b24a6 --- /dev/null +++ b/.agents/skills/coss/references/primitives/avatar.md @@ -0,0 +1,78 @@ +# coss Avatar + +## When to use + +- Identity visuals for users/teams in compact spaces. +- Image + fallback initials patterns in cards, lists, and menus. + +## Install + +```bash +npx shadcn@latest add @coss/avatar +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +``` + +## Minimal pattern + +```tsx + + + JD + +``` + +## Patterns from coss particles + +### Key patterns + +Custom size via Tailwind class: + +```tsx + + + JD + +``` + +Stacked avatar group: + +```tsx +
+ + + U1 + + + + U2 + +
+``` + +### More examples + +See `p-avatar-1` through `p-avatar-5` for sizes, radii, fallback-only, and group patterns. + +## Common pitfalls + +- Omitting `AvatarFallback`, causing broken image states with no identity fallback. +- Using non-descriptive `alt` text on `AvatarImage` in accessible contexts. +- Relying on oversized custom wrappers instead of built-in size variants/classes. + +## Useful particle references + +- fallback only: `p-avatar-2` +- different sizes: `p-avatar-3` +- different radius: `p-avatar-4` +- group avatars: `p-avatar-5` diff --git a/.agents/skills/coss/references/primitives/badge.md b/.agents/skills/coss/references/primitives/badge.md new file mode 100644 index 0000000..cb47a1c --- /dev/null +++ b/.agents/skills/coss/references/primitives/badge.md @@ -0,0 +1,79 @@ +# coss Badge + +## When to use + +- Short status/category labels and counts. +- Inline metadata chips paired with buttons, tables, and cards. + +## Install + +```bash +npx shadcn@latest add @coss/badge +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Badge } from "@/components/ui/badge" +``` + +## Minimal pattern + +```tsx +Badge +``` + +## Patterns from coss particles + +### Key patterns + +Variants via the `variant` prop: + +```tsx +Default +Outline +Secondary +Destructive +Info +Success +Warning +Error +``` + +Badge with decorative icon: + +```tsx + + +``` + +Badge inside a button (use negative margin for alignment): + +```tsx + +``` + +### More examples + +See `p-badge-1` through `p-badge-9` for all variant/size combinations. + +## Common pitfalls + +- Using badge as interactive button without proper button semantics. +- Applying raw palette classes instead of semantic tokens/variants for status. +- Overloading badge content with long text that should be normal body copy. + +## Useful particle references + +See `p-badge-1` through `p-badge-9` for all variants (outline, secondary, destructive, info, success, warning, error, small). diff --git a/.agents/skills/coss/references/primitives/breadcrumb.md b/.agents/skills/coss/references/primitives/breadcrumb.md new file mode 100644 index 0000000..5e9225c --- /dev/null +++ b/.agents/skills/coss/references/primitives/breadcrumb.md @@ -0,0 +1,91 @@ +# coss Breadcrumb + +## When to use + +- Hierarchy/location indicators for current page context. +- Compact navigation trails for nested routes and detail pages. + +## Install + +```bash +npx shadcn@latest add @coss/breadcrumb +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { + Breadcrumb, + BreadcrumbEllipsis, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb" +``` + +## Minimal pattern + +```tsx + + + + Home + + + + + + + Components + + + + Breadcrumb + + + +``` + +## Patterns from coss particles + +### Key patterns + +Icon-only home link (requires `aria-label`): + +```tsx + + + + +``` + +Custom separator: + +```tsx + + + +``` + +### More examples + +See `p-breadcrumb-1` and `p-breadcrumb-2` for default and custom separator patterns. + +## Common pitfalls + +- Using breadcrumb as primary nav menu instead of contextual trail. +- Omitting `aria-label` on icon-only breadcrumb items. +- Adding deep breadcrumb chains without meaningful hierarchy. + +## Useful particle references + +- with custom separator: `p-breadcrumb-2` diff --git a/.agents/skills/coss/references/primitives/button.md b/.agents/skills/coss/references/primitives/button.md new file mode 100644 index 0000000..825fd1e --- /dev/null +++ b/.agents/skills/coss/references/primitives/button.md @@ -0,0 +1,109 @@ +# coss Button + +## When to use + +- Primary and secondary action triggers. +- Icon, loading, and shortcut-enhanced actions across forms and toolbars. + +## Install + +```bash +npx shadcn@latest add @coss/button +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Button } from "@/components/ui/button" +``` + +## Minimal pattern + +```tsx + +``` + +`Button` defaults to `type="button"` when rendered as the native button part. In form flows, set `type` explicitly (`button` / `submit` / `reset`) to match intent, especially when using `render` composition. + +## Patterns from coss particles + +### Key patterns + +Variants are set via the `variant` prop: + +```tsx + + + + + + +``` + +Icon-only button (always needs `aria-label`): + +```tsx + +``` + +Button with icon and text (no opacity on the icon): + +```tsx + +``` + +Sizes: `xs`, `sm`, `default`, `lg`, `icon-xs`, `icon-sm`, `icon`, `icon-lg`. + +### Loading state + +Built-in `loading` prop (disables and shows spinner automatically): + +```tsx + +``` + +Composite approach (manual `Spinner` + `disabled`): + +```tsx + +``` + +Prefer the `loading` prop for typical async actions. Use the composite approach when you need custom spinner placement or label. + +### More examples + +- default: `p-button-1` +- outline: `p-button-2` +- secondary: `p-button-3` +- destructive: `p-button-4` +- destructive outline: `p-button-5` +- ghost: `p-button-6` +- link: `p-button-7` +- extra-small size: `p-button-8` + +## Common pitfalls + +- Omitting explicit `type` inside forms/dialogs and triggering unintended submit behavior. +- Using icon-only buttons without `aria-label` on the button. +- Rebuilding button state styling with ad-hoc classes instead of variants/sizes. +- Using `SelectButton` as if it were a general-purpose `Button`; `SelectButton` is a select-flavored trigger helper and should be treated as a `select`/`combobox` pattern. + +## Useful particle references + +- variants/sizes: `p-button-1` through `p-button-8` +- composite loading (Spinner + disabled): `p-button-18` +- built-in loading prop: `p-button-41` diff --git a/.agents/skills/coss/references/primitives/calendar.md b/.agents/skills/coss/references/primitives/calendar.md new file mode 100644 index 0000000..8b86f08 --- /dev/null +++ b/.agents/skills/coss/references/primitives/calendar.md @@ -0,0 +1,68 @@ +# coss Calendar + +## When to use + +- Date selection interfaces and calendar-based scheduling UIs. +- Single-date, range, and constrained date picking patterns. + +## Install + +```bash +npx shadcn@latest add @coss/calendar +``` + +Manual deps from docs: + +```bash +npm install react-day-picker +``` + +## Canonical imports + +```tsx +import { Calendar } from "@/components/ui/calendar" +``` + +## Minimal pattern + +```tsx + +``` + +## Patterns from coss particles + +### Key patterns + +Single date selection with state: + +```tsx +const [date, setDate] = useState() + + +``` + +Date range selection: + +```tsx +const [range, setRange] = useState() + + +``` + +### More examples + +See `p-calendar-1` through `p-calendar-6` for single, range, dropdown navigation, and month/year select patterns. + +## Common pitfalls + +- Using calendar for free-text date input flows better handled by date fields. +- Missing locale/disabled-date constraints for business rules. +- Treating calendar as date-time picker without explicit time UI. + +## Useful particle references + +- single date selection: `p-calendar-2` +- date range selection: `p-calendar-3` +- dropdown navigation: `p-calendar-4` +- select dropdown for month/year: `p-calendar-5` +- combobox dropdown for month/year: `p-calendar-6` diff --git a/.agents/skills/coss/references/primitives/card.md b/.agents/skills/coss/references/primitives/card.md new file mode 100644 index 0000000..40d79b6 --- /dev/null +++ b/.agents/skills/coss/references/primitives/card.md @@ -0,0 +1,85 @@ +# coss Card + +## When to use + +- Structured surface sections for grouped content. +- Settings, dashboard, and preview layouts with header/panel/footer semantics. + +## Install + +```bash +npx shadcn@latest add @coss/card +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { + Card, + CardDescription, + CardFooter, + CardHeader, + CardPanel, + CardTitle, +} from "@/components/ui/card" +``` + +## Minimal pattern + +```tsx + + + Title + Description + + Content + Footer + +``` + +## Patterns from coss particles + +### Key patterns + +Card with form content: + +```tsx + + + Create project + Deploy your new project in one click. + + + + Name + + + + + + + + +``` + +Keep `CardHeader`, `CardPanel`, and `CardFooter` as direct children of `Card` to preserve built-in spacing and layout. + +### More examples + +See `p-card-1` through `p-card-8` for various card compositions. + +## Common pitfalls + +- Skipping `CardHeader`/`CardPanel`/`CardFooter` structure in composed cards. +- Mixing unrelated layout wrappers that break spacing between card sections. +- Using cards as generic wrappers when `Frame` or plain layout would be clearer. + +## Useful particle references + +- core patterns: `p-card-1`, `p-card-2`, `p-card-3`, `p-card-4`, `p-card-5`, `p-card-6`, `p-card-7`, `p-card-8` diff --git a/.agents/skills/coss/references/primitives/checkbox-group.md b/.agents/skills/coss/references/primitives/checkbox-group.md new file mode 100644 index 0000000..873201b --- /dev/null +++ b/.agents/skills/coss/references/primitives/checkbox-group.md @@ -0,0 +1,95 @@ +# coss Checkbox Group + +## When to use + +- Multi-select option groups with shared label context. +- Collecting multiple values under one field name. + +## Install + +```bash +npx shadcn@latest add @coss/checkbox-group +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Checkbox } from "@/components/ui/checkbox" +import { CheckboxGroup } from "@/components/ui/checkbox-group" +``` + +## Minimal pattern + +```tsx + + + + + +``` + +For form-bound option groups, prefer `Field` + `Fieldset` composition so legend, labels, and errors are grouped correctly. + +## Patterns from coss particles + +### Key patterns + +Group with `aria-label` and `defaultValue`: + +```tsx + + + + +``` + +Controlled group: + +```tsx +const [value, setValue] = useState(["next"]) + + + ... + +``` + +For form-bound groups, use `Field` + `Fieldset` so legend, labels, and errors are grouped correctly. + +### More examples + +See `p-checkbox-group-1` through `p-checkbox-group-5` for disabled items, parent checkbox, nested, and form patterns. + +## Common pitfalls + +- Using checkbox group when only one option should be selected. +- Missing group label/legend context for assistive technology. +- Incorrectly handling submitted values as scalar instead of array/list. + +## Useful particle references + +- with disabled item: `p-checkbox-group-2` +- parent checkbox: `p-checkbox-group-3` +- nested parent checkbox: `p-checkbox-group-4` +- form integration: `p-checkbox-group-5` +- form composition references: `p-form-1`, `p-form-2`, `p-input-group-24` diff --git a/.agents/skills/coss/references/primitives/checkbox.md b/.agents/skills/coss/references/primitives/checkbox.md new file mode 100644 index 0000000..a00c778 --- /dev/null +++ b/.agents/skills/coss/references/primitives/checkbox.md @@ -0,0 +1,95 @@ +# coss Checkbox + +## When to use + +- Single boolean consent/selection controls. +- Standalone yes/no options with explicit labeling. + +## When NOT to use + +- If the control is a preference toggle (on/off) in settings -> use Switch instead. +- If selecting from mutually exclusive options -> use RadioGroup instead. +- If multiple checkboxes share grouped state -> use CheckboxGroup instead. + +## Install + +```bash +npx shadcn@latest add @coss/checkbox +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Checkbox } from "@/components/ui/checkbox" +``` + +## Minimal pattern + +```tsx + +``` + +## Patterns from coss particles + +### Key patterns + +CheckboxGroup with label-wrapped options: + +```tsx + + + + + +``` + +Checkbox with description text (use `id`/`htmlFor`): + +```tsx +const id = useId() + +
+ +
+ +

+ By clicking this checkbox, you agree to the terms. +

+
+
+``` + +### More examples + +- disabled: `p-checkbox-2` +- with description: `p-checkbox-3` +- card style: `p-checkbox-4` +- form integration: `p-checkbox-5` + +## Common pitfalls + +- Using checkbox for exclusive single-choice options that should be radios. +- Missing visible label association (`Label` or `FieldLabel`) for each checkbox. +- Treating `onCheckedChange` values as plain boolean without handling indeterminate where relevant. + +## Useful particle references + +See `p-checkbox-1` through `p-checkbox-5` for label wrapping, disabled, description, card, and form patterns. diff --git a/.agents/skills/coss/references/primitives/collapsible.md b/.agents/skills/coss/references/primitives/collapsible.md new file mode 100644 index 0000000..bc3cfb0 --- /dev/null +++ b/.agents/skills/coss/references/primitives/collapsible.md @@ -0,0 +1,70 @@ +# coss Collapsible + +## When to use + +- Progressive disclosure of optional content. +- Expandable help/settings sections without leaving the page. + +## Install + +```bash +npx shadcn@latest add @coss/collapsible +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Collapsible, + CollapsiblePanel, + CollapsibleTrigger, +} from "@/components/ui/collapsible" +``` + +## Minimal pattern + +```tsx + + Can I access the file in the cloud? + + Yes, you can access the file in the cloud. + + +``` + +## Patterns from coss particles + +### Key patterns + +Controlled collapsible: + +```tsx +const [open, setOpen] = useState(false) + + + + {open ? "Hide details" : "Show details"} + + Hidden content here. + +``` + +### More examples + +See `p-collapsible-1` for the core pattern. + +## Common pitfalls + +- Placing trigger/panel outside the same collapsible root. +- Assuming panel content is always visible/mounted for dependent logic. +- Using modal-like interactions where collapsible disclosure is more appropriate. + +## Useful particle references + +- core patterns: `p-collapsible-1` diff --git a/.agents/skills/coss/references/primitives/combobox.md b/.agents/skills/coss/references/primitives/combobox.md new file mode 100644 index 0000000..aa965a6 --- /dev/null +++ b/.agents/skills/coss/references/primitives/combobox.md @@ -0,0 +1,114 @@ +# coss Combobox + +## When to use + +- Searchable selection combining text input and list selection. +- Rich option rows with filtering and custom trigger behavior. + +## When NOT to use + +- If options are few and fixed (no search needed) -> use Select instead. +- If you need free-form text suggestions without strict selection -> use Autocomplete instead. +- If the user picks from a simple short list -> use RadioGroup or Select. + +## Install + +```bash +npx shadcn@latest add @coss/combobox +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Combobox, + ComboboxClear, + ComboboxCollection, + ComboboxEmpty, + ComboboxGroup, + ComboboxGroupLabel, + ComboboxInput, + ComboboxItem, + ComboboxList, + ComboboxPopup, + ComboboxSeparator, + ComboboxValue, + useComboboxFilter, +} from "@/components/ui/combobox" +``` + +## Minimal pattern + +```tsx +const items = [ + { value: "apple", label: "Apple" }, + { value: "banana", label: "Banana" }, + { value: "orange", label: "Orange" }, + { value: "grape", label: "Grape" }, +] + + + + + No results found. + + {(item) => {item.label}} + + + +``` + +For form-bound comboboxes, prefer `Field` composition (`Field` + `FieldLabel` + `FieldError`) instead of standalone controls. + +## Patterns from coss particles + +- **Portal forwarding**: optional `portalProps` on `ComboboxPopup` → Base UI `Combobox.Portal` (`keepMounted`, `container`, …). See [portal-props.md](../portal-props.md). + +### Key patterns + +Combobox with label in a Field: + +```tsx + + Framework + + + + No results found. + + {(item) => {item.label}} + + + + +``` + +Sizes: `sm`, `default`, `lg` on `ComboboxInput`. + +### More examples + +See `p-combobox-1` through `p-combobox-9` for sizes, label, auto-highlight, clear button, groups, and multiple selection. + +## Common pitfalls + +- Mixing select and combobox APIs without validating item/value wiring. +- Using object values without stable string serialization where needed. +- Missing empty/loading states for remote or filtered datasets. + +## Useful particle references + +- disabled: `p-combobox-2` +- small size: `p-combobox-3` +- large size: `p-combobox-4` +- with label: `p-combobox-5` +- auto highlight: `p-combobox-6` +- with clear button: `p-combobox-7` +- with groups: `p-combobox-8` +- with multiple selection: `p-combobox-9` +- related search/selection references: `p-autocomplete-1`, `p-select-1`, `p-input-group-1` diff --git a/.agents/skills/coss/references/primitives/command.md b/.agents/skills/coss/references/primitives/command.md new file mode 100644 index 0000000..b46959b --- /dev/null +++ b/.agents/skills/coss/references/primitives/command.md @@ -0,0 +1,121 @@ +# coss Command + +## When to use + +- Command palette and keyboard-navigable action menus. +- Fast action discovery for power-user and app shortcut workflows. + +## When NOT to use + +- If the list is a simple set of actions without search -> use Menu instead. +- If the user is selecting from a predefined list -> use Select or Combobox instead. +- If the flow is a data form -> use Form instead. + +## Install + +```bash +npx shadcn@latest add @coss/command +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Command, + CommandCollection, + CommandDialog, + CommandDialogPopup, + CommandDialogTrigger, + CommandEmpty, + CommandFooter, + CommandGroup, + CommandGroupLabel, + CommandInput, + CommandItem, + CommandList, + CommandPanel, + CommandSeparator, + CommandShortcut, +} from "@/components/ui/command" +import { Button } from "@/components/ui/button" +``` + +## Minimal pattern + +```tsx +const items = [ + { value: "linear", label: "Linear" }, + { value: "figma", label: "Figma" }, + { value: "slack", label: "Slack" }, +] + + + }> + Open Command Palette + + + + + + No results found. + + {(item) => ( + + {item.label} + + )} + + + + +``` + +## Patterns from coss particles + +- **Portal forwarding**: optional `portalProps` on `CommandDialogPopup` → Base UI `Dialog.Portal` (`keepMounted`, `container`, …). See [portal-props.md](../portal-props.md). + +### Key patterns + +Command with grouped sections: + +```tsx + + + No results found. + + + Suggestions + + {(item) => ( + + {item.label} + + )} + + + + +``` + +Use `CommandDialog` + `CommandDialogTrigger` + `CommandDialogPopup` to wrap `Command` in a dialog overlay. Use controlled `open`/`onOpenChange` state for keyboard-shortcut activation. + +### More examples + +See `p-command-1` and `p-command-2` for dialog palette and grouped action patterns. + +## Common pitfalls + +- Using command list without clear grouping and action labels. +- Binding critical destructive actions without confirmation pathway. +- Missing keyboard accessibility checks for arrow/select/escape interactions. + +## Useful particle references + +- core patterns: `p-command-1`, `p-command-2` +- related search/selection references: `p-autocomplete-1`, `p-select-1`, `p-input-group-1` diff --git a/.agents/skills/coss/references/primitives/dialog.md b/.agents/skills/coss/references/primitives/dialog.md new file mode 100644 index 0000000..3fb1401 --- /dev/null +++ b/.agents/skills/coss/references/primitives/dialog.md @@ -0,0 +1,93 @@ +# coss Dialog + +## When to use + +- Modal overlays that require user focus and explicit action. +- Multi-section popup flows with header/body/footer structure. + +## When NOT to use + +- If the overlay should slide from the edge -> use Sheet or Drawer instead. +- If the interaction is a destructive confirmation -> use AlertDialog instead. +- If the content is non-blocking contextual info -> use Popover instead. + +## Install + +```bash +npx shadcn@latest add @coss/dialog +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Dialog, + DialogClose, + DialogDescription, + DialogFooter, + DialogHeader, + DialogPanel, + DialogPopup, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +``` + +## Minimal pattern + +```tsx + + }>Open Dialog + + + Dialog Title + Dialog Description + + Content + + }>Close + + + +``` + +## Patterns from coss particles + +- **Portal forwarding**: optional `portalProps` on `DialogPopup` → Base UI `Dialog.Portal` (`keepMounted`, `container`, …). See [portal-props.md](../portal-props.md). +- **Section structure invariant**: keep `DialogHeader`, `DialogPanel`, and `DialogFooter` as direct sections in `DialogPopup` to preserve built-in layout/styling behavior. +- **Form in dialog**: keep **`DialogHeader`** outside the form; wrap **`DialogPanel`** + **`DialogFooter`** in **`
`** (or native ``) so the popup’s flex column treats header, panel, and footer as direct layout sections. +- **Action buttons**: use `DialogClose` with `render={ + }>Cancel + + + +``` + +Responsive drawer + dialog (drawer on mobile, dialog on desktop): see `p-drawer-12`. + +### More examples + +See `p-drawer-1` through `p-drawer-13` for inset, straight, scrollable, nested, snap points, mobile menu, and responsive patterns. + +## Common pitfalls + +- Using drawer for desktop modal flows where dialog/sheet is clearer. +- Forgetting responsive switch logic when drawer is mobile-only variant. +- Breaking section layout by putting the whole dialog in a block-level ``; prefer **header outside**, **`Form className="contents"`** around **panel + footer** (see dialog/form skills). + +## Useful particle references + +- inset variant: `p-drawer-4` +- straight variant: `p-drawer-5` +- scrollable content: `p-drawer-6` +- nested drawers: `p-drawer-7` +- snap points: `p-drawer-9` +- mobile menu: `p-drawer-11` +- responsive dialog: `p-drawer-12` +- responsive menu: `p-drawer-13` +- cross-overlay references: `p-dialog-1`, `p-popover-1`, `p-menu-2` diff --git a/.agents/skills/coss/references/primitives/empty.md b/.agents/skills/coss/references/primitives/empty.md new file mode 100644 index 0000000..9401dc2 --- /dev/null +++ b/.agents/skills/coss/references/primitives/empty.md @@ -0,0 +1,88 @@ +# coss Empty + +## When to use + +- No-data/no-results states with guidance. +- Action-oriented recovery UIs when content lists are empty. + +## Install + +```bash +npx shadcn@latest add @coss/empty +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { + Empty, + EmptyContent, + EmptyDescription, + EmptyHeader, + EmptyMedia, + EmptyTitle, +} from "@/components/ui/empty" +``` + +## Minimal pattern + +```tsx + + + + + + No data + No data found + + + + + +``` + +## Patterns from coss particles + +### Key patterns + +Empty state with icon and action: + +```tsx + + + + + No projects yet + Get started by creating your first project. + + + + + +``` + +Always include an actionable next step (button, link) in `EmptyContent`. + +### More examples + +See `p-empty-1` for the core pattern. + +## Common pitfalls + +- Presenting empty states without actionable next step. +- Using empty state component for loading/error states instead of dedicated primitives. +- Copy-only empty states with no context-specific guidance for recovery. + +## Useful particle references + +- core patterns: `p-empty-1` diff --git a/.agents/skills/coss/references/primitives/field.md b/.agents/skills/coss/references/primitives/field.md new file mode 100644 index 0000000..9c18e7f --- /dev/null +++ b/.agents/skills/coss/references/primitives/field.md @@ -0,0 +1,101 @@ +# coss Field + +## When to use + +- Accessible field wrappers with labels, descriptions, and errors. +- Form control state wiring (`invalid`, `required`, touched/error messaging). + +## Install + +```bash +npx shadcn@latest add @coss/field +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Field, + FieldDescription, + FieldError, + FieldLabel, + FieldValidity, +} from "@/components/ui/field" +import { Input } from "@/components/ui/input" +``` + +## Minimal pattern + +```tsx + + Name + + Visible on your profile + Please enter a valid name + + {(validity) => ( + {validity.error &&

{validity.error}

} + )} +
+
+``` + +## Patterns from coss particles + +### Key patterns + +Required field with error: + +```tsx + + Email * + + We'll never share your email. + Please enter a valid email. + +``` + +Field wrapping an autocomplete: + +```tsx + + Framework + + + + + {(item) => {item.label}} + + + + + +``` + +### More examples + +See `p-field-1` through `p-field-9` for required, disabled, error, validity, input-group, autocomplete, and combobox field patterns. + +## Common pitfalls + +- Rendering errors detached from the related control, breaking context. +- Missing `name` in form flows, causing silent submit omissions. +- Using field wrapper without corresponding label/description/error semantics. + +## Useful particle references + +- required field: `p-field-2` +- disabled field: `p-field-3` +- with error: `p-field-4` +- with validity: `p-field-5` +- input group: `p-field-6` +- autocomplete field: `p-field-7` +- combobox field: `p-field-8` +- combobox multiple field: `p-field-9` +- form composition references: `p-form-1`, `p-form-2`, `p-input-group-24` diff --git a/.agents/skills/coss/references/primitives/fieldset.md b/.agents/skills/coss/references/primitives/fieldset.md new file mode 100644 index 0000000..45a42ea --- /dev/null +++ b/.agents/skills/coss/references/primitives/fieldset.md @@ -0,0 +1,69 @@ +# coss Fieldset + +## When to use + +- Grouped related controls under one legend/description. +- Complex forms requiring semantic grouping for radios/checkboxes. + +## Install + +```bash +npx shadcn@latest add @coss/fieldset +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Fieldset, FieldsetLegend } from "@/components/ui/fieldset" +``` + +## Minimal pattern + +```tsx +
+ Fieldset legend +
+``` + +## Patterns from coss particles + +### Key patterns + +Fieldset grouping related fields: + +```tsx +
+ Personal Information + + First name + + + + Last name + + +
+``` + +Always include `FieldsetLegend` as the accessible group heading. + +### More examples + +See `p-fieldset-1` for the core pattern. + +## Common pitfalls + +- Using ad-hoc div wrappers instead of semantic fieldset for grouped controls. +- Omitting `FieldsetLegend`, reducing accessibility context. +- Placing unrelated controls inside one fieldset, hurting form clarity. + +## Useful particle references + +- core patterns: `p-fieldset-1` +- form composition references: `p-form-1`, `p-form-2`, `p-input-group-24` diff --git a/.agents/skills/coss/references/primitives/form.md b/.agents/skills/coss/references/primitives/form.md new file mode 100644 index 0000000..5278994 --- /dev/null +++ b/.agents/skills/coss/references/primitives/form.md @@ -0,0 +1,79 @@ +# coss Form + +## When to use + +- Structured form validation and submission flows. +- Forms with field-level labels, descriptions, and errors. +- Integrations with external form libraries (for example React Hook Form / TanStack Form). + +## Install + +```bash +npx shadcn@latest add @coss/form +``` + +Manual deps: + +```bash +npm install @base-ui/react zod +``` + +## Canonical imports + +```tsx +import { + Field, + FieldDescription, + FieldError, + FieldLabel, +} from "@/components/ui/field" +import { Form } from "@/components/ui/form" +import { Input } from "@/components/ui/input" +``` + +## Minimal pattern + +```tsx + {/* handle submit */}}> + + Email + + Used for account updates + Please enter a valid email. + +
+``` + +## Patterns from coss particles + +- `Form` usage in particles is intentionally lightweight; use the Base UI forms handbook patterns below for deeper validation/library integrations. +- **Dialog / sheet / drawer:** Header **outside** the form; **`Form className="contents"`** wraps **panel + footer** only (`p-dialog-1`, `p-sheet-1`, `p-sheet-2`, `p-drawer-10`, `p-drawer-12`). + +## Patterns from coss/Base UI forms + +- **Submission mode**: use `onSubmit` for native `FormData` handling; use `onFormSubmit` when you want parsed form values object from Base UI Form. +- **Field naming**: set `name` on each field/control flow so values are included in submission. +- **Accessible names**: prefer visible labels (`FieldLabel`, `SelectLabel`, etc.); use `aria-label` only when no visible label exists. +- **Grouped controls**: for radio/checkbox groups or multi-control sections, use fieldset-style grouping (`Fieldset` + `Field.Item`) instead of ad-hoc wrappers. +- **Validation rendering**: pair constraints/custom validation with `FieldError`; keep error output semantically tied to the same field. +- **Textarea integration**: use coss `Textarea` directly inside `Field`; it already integrates with Base UI field control semantics, so `FieldControl render={...}` is not required for standard textarea usage. +- **External library integration**: when using RHF/TanStack, forward refs/input refs to the underlying control and map invalid/touched/dirty state into `Field`. + +## Common pitfalls + +- Using `Form` without field-level structure (`Field`, label, error). +- Missing control `name` (field not present in form submission payload). +- Missing input `type` and button `type`. +- Showing validation messages without matching invalid semantics. +- Using grouped checkboxes/radios without proper group legend/structure. +- Not forwarding refs in RHF/TanStack integration, which breaks focus-on-error behavior. + +## Useful particle references + +- `p-form-1` (basic integration) +- `p-form-2` (zod usage) + +## Further reading + +- Base UI Forms handbook: `https://base-ui.com/react/handbook/forms.md` + diff --git a/.agents/skills/coss/references/primitives/frame.md b/.agents/skills/coss/references/primitives/frame.md new file mode 100644 index 0000000..2e05709 --- /dev/null +++ b/.agents/skills/coss/references/primitives/frame.md @@ -0,0 +1,82 @@ +# coss Frame + +## When to use + +- Bordered app surfaces around content blocks. +- Container wrapper for data components like table, cards, and panes. + +## Install + +```bash +npx shadcn@latest add @coss/frame +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { + Frame, + FrameDescription, + FrameFooter, + FrameHeader, + FramePanel, + FrameTitle, +} from "@/components/ui/frame" +``` + +## Minimal pattern + +```tsx + + + Title + Description + + Content + Footer + +``` + +## Patterns from coss particles + +### Key patterns + +Frame with header actions: + +```tsx + + +
+ Users + Manage team members. +
+ +
+ {/* Table or list content */} + +

Showing 1-10 of 100

+
+ +``` + +Use `Frame` to normalize border/radius around heterogeneous inner content (tables, lists, cards). + +### More examples + +See `p-frame-1` through `p-frame-3` for basic and separated panel patterns. + +## Common pitfalls + +- Using nested frames excessively, causing dense double borders. +- Applying frame as layout grid replacement instead of content surface wrapper. +- Forgetting to align inner component width expectations (table/list full width). + +## Useful particle references + +- separated panels: `p-frame-3` diff --git a/.agents/skills/coss/references/primitives/group.md b/.agents/skills/coss/references/primitives/group.md new file mode 100644 index 0000000..323faf7 --- /dev/null +++ b/.agents/skills/coss/references/primitives/group.md @@ -0,0 +1,84 @@ +# coss Group + +## When to use + +- Connected controls with shared visual boundary. +- Composed action clusters using buttons, toggles, and menu triggers. + +## Install + +```bash +npx shadcn@latest add @coss/group +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Button } from "@/components/ui/button" +import { Group, GroupSeparator } from "@/components/ui/group" +``` + +## Minimal pattern + +```tsx + + + + + +``` + +## Patterns from coss particles + +### Key patterns + +Button group with separator: + +```tsx + + + + + + + +``` + +Group with input and button: + +```tsx + + + + + +``` + +Sizes: `sm`, `default`, `lg`. Orientation: vertical via `orientation="vertical"`. + +### More examples + +See `p-group-1` through `p-group-9` for input, sizes, disabled, vertical, and labeled text patterns. + +## Common pitfalls + +- Forgetting `GroupSeparator` between controls in connected groups. +- Mixing control sizes/variants that break shared group silhouette. +- Using standalone controls where a grouped action model is expected. + +## Useful particle references + +- with input: `p-group-2` +- small size: `p-group-3` +- large size: `p-group-4` +- with disabled button: `p-group-5` +- with default buttons: `p-group-6` +- with start labeled text: `p-group-7` +- with end text: `p-group-8` +- vertical: `p-group-9` diff --git a/.agents/skills/coss/references/primitives/input-group.md b/.agents/skills/coss/references/primitives/input-group.md new file mode 100644 index 0000000..860b8cb --- /dev/null +++ b/.agents/skills/coss/references/primitives/input-group.md @@ -0,0 +1,104 @@ +# coss Input Group + +## When to use + +- Inputs/textareas that need inline or block addons. +- Input composition with icons, buttons, labels, badges, and shortcuts. + +## Install + +```bash +npx shadcn@latest add @coss/input-group +``` + +Manual deps: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + InputGroup, + InputGroupAddon, + InputGroupInput, + InputGroupText, +} from "@/components/ui/input-group" +``` + +## Minimal pattern + +```tsx + + + + + + +``` + +## Critical invariant + +`InputGroupAddon` must be after `InputGroupInput`/`InputGroupTextarea` in DOM order for proper focus behavior. + +## Patterns from coss particles + +### Key patterns + +Search input with icon: + +```tsx + + + + + +``` + +URL prefix with text addon: + +```tsx + + + https:// + + + +``` + +Input with interactive button addon: + +```tsx + + + + + + +``` + +`InputGroupAddon` must be after `InputGroupInput`/`InputGroupTextarea` in DOM order for proper focus behavior. + +### More examples + +See `p-input-group-1` through `p-input-group-29` for icons, text prefixes, interactive addons, textarea layouts, and stateful patterns. + +## Common pitfalls + +- Using `Input`/`Textarea` directly instead of `InputGroupInput`/`InputGroupTextarea`. +- Wrong addon DOM order. +- Missing explicit input types. + +## Useful particle references + +- icon addon + search baseline: `p-input-group-1`, `p-input-group-2`, `p-input-group-13`, `p-input-group-14`, `p-input-group-20` +- prefix/suffix text addons (url/domain/currency): `p-input-group-3`, `p-input-group-4`, `p-input-group-5`, `p-input-group-6` +- interactive addons (buttons/badge/kbd/menu/clear/voice): `p-input-group-7`, `p-input-group-8`, `p-input-group-9`, `p-input-group-10`, `p-input-group-11`, `p-input-group-12`, `p-input-group-18`, `p-input-group-21`, `p-input-group-22`, `p-input-group-23` +- textarea/editor-style layouts with block-end/start addons: `p-input-group-17`, `p-input-group-19`, `p-input-group-27`, `p-input-group-28`, `p-input-group-29` +- stateful/validation patterns (disabled/loading/password strength): `p-input-group-15`, `p-input-group-16`, `p-input-group-24`, `p-input-group-26` + diff --git a/.agents/skills/coss/references/primitives/input.md b/.agents/skills/coss/references/primitives/input.md new file mode 100644 index 0000000..02b0794 --- /dev/null +++ b/.agents/skills/coss/references/primitives/input.md @@ -0,0 +1,80 @@ +# coss Input + +## When to use + +- Single-line text entry with variants and addons. +- Email/password/search/file and other typed input flows. + +## Install + +```bash +npx shadcn@latest add @coss/input +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Input } from "@/components/ui/input" +``` + +## Minimal pattern + +```tsx + +``` + +Always set `type` explicitly on `Input` (`text`, `email`, `password`, `search`, `file`, etc.). Do not rely on browser defaults. +For form fields, prefer wrapping `Input` with `Field` + `FieldLabel` + `FieldError` instead of standalone usage. + +## Patterns from coss particles + +### Key patterns + +Field-wrapped input (preferred for forms): + +```tsx + + Email + + Please enter a valid email. + +``` + +Input with addon (InputGroup): + +```tsx + + + + + +``` + +Sizes: `sm`, `default`, `lg`. Always specify `type` explicitly. + +### More examples + +- small size: `p-input-2` +- large size: `p-input-3` +- disabled: `p-input-4` +- file: `p-input-5` +- with label: `p-input-6` +- with button: `p-input-7` +- form integration: `p-form-1` + +## Common pitfalls + +- Omitting explicit `type` and relying on browser defaults. +- Using icon-only affordances without label/aria context. +- Applying heavy class overrides before using built-in size/variant props. + +## Useful particle references + +See `p-input-1` through `p-input-7` for size/variant combinations, `p-form-1` for form integration. diff --git a/.agents/skills/coss/references/primitives/kbd.md b/.agents/skills/coss/references/primitives/kbd.md new file mode 100644 index 0000000..48cf003 --- /dev/null +++ b/.agents/skills/coss/references/primitives/kbd.md @@ -0,0 +1,78 @@ +# coss Kbd + +## When to use + +- Keyboard shortcut keycaps near commands. +- Single or grouped key hint display in action UIs. + +## Install + +```bash +npx shadcn@latest add @coss/kbd +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { Kbd, KbdGroup } from "@/components/ui/kbd" +``` + +## Minimal pattern + +```tsx +K +``` + +## Patterns from coss particles + +### Key patterns + +Multi-key shortcut with `KbdGroup` (each key gets its own `Kbd`): + +```tsx + + + K + +``` + +Button with keyboard shortcut: + +```tsx + +``` + +Single key shortcut in a button: + +```tsx + +``` + +### More examples + +- input group integration: `p-input-group-11` + +## Common pitfalls + +- Placing multi-key sequences in a single `Kbd` when `KbdGroup` is clearer. +- Using decorative keycaps without tying them to nearby actionable controls. +- Overusing kbd hints in simple UIs, adding noise instead of clarity. + +## Useful particle references + +- input group: `p-input-group-11` diff --git a/.agents/skills/coss/references/primitives/label.md b/.agents/skills/coss/references/primitives/label.md new file mode 100644 index 0000000..9c75cc5 --- /dev/null +++ b/.agents/skills/coss/references/primitives/label.md @@ -0,0 +1,71 @@ +# coss Label + +## When to use + +- Visible accessible labels for inputs and controls. +- Simple `htmlFor`/`id` associations in forms and settings UIs. + +## Install + +```bash +npx shadcn@latest add @coss/label +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { Label } from "@/components/ui/label" +``` + +## Minimal pattern + +```tsx + +``` + +## Patterns from coss particles + +### Key patterns + +Label paired with input: + +```tsx +const id = useId() + +
+ + +
+``` + +Label wrapping a checkbox: + +```tsx + +``` + +Prefer `FieldLabel` within `Field` for validation-aware forms. + +### More examples + +See `p-field-1`, `p-input-1`, `p-checkbox-1` for label-in-form patterns. + +## Common pitfalls + +- Using `aria-label` when visible `Label` text exists and can be associated. +- Mismatching `htmlFor`/`id` between label and control. +- Using label component as generic typography instead of form labeling. + +## Useful particle references + +- with checkbox: `checkbox-demo` +- label-specific particles: no dedicated `p-label-*` family; use form references `p-field-1`, `p-input-1`, `p-checkbox-1`. diff --git a/.agents/skills/coss/references/primitives/menu.md b/.agents/skills/coss/references/primitives/menu.md new file mode 100644 index 0000000..0d63d04 --- /dev/null +++ b/.agents/skills/coss/references/primitives/menu.md @@ -0,0 +1,97 @@ +# coss Menu + +## When to use + +- Contextual action lists and dropdown commands. +- Mixed item types (regular, checkbox, radio, nested submenu). + +## When NOT to use + +- If the user needs to search/filter actions -> use Command instead. +- If the content is rich informational (not actions) -> use Popover instead. +- If the overlay is a full modal flow -> use Dialog instead. + +## Install + +```bash +npx shadcn@latest add @coss/menu +``` + +Manual deps and theme var from docs: + +```bash +npm install @base-ui/react +``` + +Also include the destructive foreground CSS variable snippet from the coss menu docs when doing manual setup. + +## Canonical imports + +```tsx +import { + Menu, + MenuCheckboxItem, + MenuGroup, + MenuGroupLabel, + MenuItem, + MenuPopup, + MenuRadioGroup, + MenuRadioItem, + MenuSeparator, + MenuShortcut, + MenuSub, + MenuSubPopup, + MenuSubTrigger, + MenuTrigger, +} from "@/components/ui/menu" +``` + +## Minimal pattern + +```tsx + + Open + + Profile + + Shuffle + + +``` + +Use popup positioning props like `align` / `sideOffset` only when a layout needs explicit tuning. + +## Patterns from coss particles + +- **Portal forwarding**: optional `portalProps` on `MenuPopup` → Base UI `Menu.Portal` (`keepMounted`, `container`, …). See [portal-props.md](../portal-props.md). +- Use `MenuTrigger render={ + + + +``` + +Side options: `top`, `right`, `bottom`, `left`. + +### More examples + +See `p-sheet-1` through `p-sheet-3` for inset and side sheet patterns. + +## Common pitfalls + +- Using sheet for simple tooltip/popover hints that do not need panel behavior. +- Missing close actions and focus-return verification on open/close cycle. +- Overloading sheet with multi-step form logic better handled by dedicated route/modal flow. + +## Useful particle references + +- sheet with inset: `p-sheet-2` +- side sheets: `p-sheet-3` +- cross-overlay references: `p-dialog-1`, `p-popover-1`, `p-menu-2` diff --git a/.agents/skills/coss/references/primitives/sidebar.md b/.agents/skills/coss/references/primitives/sidebar.md new file mode 100644 index 0000000..c135436 --- /dev/null +++ b/.agents/skills/coss/references/primitives/sidebar.md @@ -0,0 +1,135 @@ +# coss Sidebar + +## When to use + +- Persistent app shell navigation and grouped links. +- Collapsible/structured side navigation for dashboard layouts. + +## Install + +```bash +npx shadcn@latest add @coss/sidebar +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarGroup, + SidebarGroupAction, + SidebarGroupContent, + SidebarGroupLabel, + SidebarHeader, + SidebarInset, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarProvider, + SidebarRail, + SidebarSeparator, + SidebarTrigger, + useSidebar, +} from "@/components/ui/sidebar" +``` + +## Minimal pattern + +```tsx + + + Workspace + + + Navigation + + + + Dashboard + + + + + + + {/* Main page content */} + +``` + +## Patterns from coss particles + +### Key patterns + +Sidebar with grouped navigation and footer: + +```tsx + + + +

App Name

+
+ + + Main + + + + }> + Dashboard + + + + }> + Projects + + + + + + + + + + Settings + + + + +
+ {/* Page content */} +
+``` + +Key composition rules: +- Wrap app with `SidebarProvider` at the layout level. +- Use `SidebarContent` (not "SidebarPanel") as the scrollable body between header/footer. It uses `ScrollArea` with `fill` so flex children (e.g. `mt-auto` footers) can pin to the bottom. +- Navigation items use `SidebarMenu` > `SidebarMenuItem` > `SidebarMenuButton`. +- For link items, use `render` composition: `}>`. Do not use `asChild` -- sidebar follows the same `render` pattern as all other coss primitives. +- Use `SidebarTrigger` for the collapse/expand toggle. +- Use `SidebarInset` for the main content area next to the sidebar. +- `SidebarRail` adds a slim hover-to-expand rail in collapsed state. + +### More examples + +See `p-toolbar-1`, `p-breadcrumb-1`, `p-tabs-1`, `p-menu-1` for related app-shell patterns. + +## Common pitfalls + +- Using non-existent parts like "SidebarPanel" or "SidebarItem" -- the correct names are `SidebarContent` and `SidebarMenuItem`. +- Forgetting `SidebarProvider` wrapper, which manages collapse state and mobile responsiveness. +- Skipping the `SidebarMenu` > `SidebarMenuItem` > `SidebarMenuButton` hierarchy for nav items. +- Missing responsive collapse strategy for narrow/mobile layouts. +- Replacing `SidebarContent`'s scroll area with a raw `ScrollArea` without `fill` when the body uses `mt-auto` to pin footers—use `fill` (see scroll-area primitive docs). + +## Useful particle references + +- sidebar-specific particles: no dedicated `p-sidebar-*` family currently. +- app-shell references: `p-toolbar-1`, `p-breadcrumb-1`, `p-tabs-1`, `p-menu-1` diff --git a/.agents/skills/coss/references/primitives/skeleton.md b/.agents/skills/coss/references/primitives/skeleton.md new file mode 100644 index 0000000..b60331c --- /dev/null +++ b/.agents/skills/coss/references/primitives/skeleton.md @@ -0,0 +1,65 @@ +# coss Skeleton + +## When to use + +- Loading placeholders matching final layout density. +- Perceived-performance improvement during fetch/render latency. + +## Install + +```bash +npx shadcn@latest add @coss/skeleton +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { Skeleton } from "@/components/ui/skeleton" +``` + +## Minimal pattern + +```tsx + +``` + +## Patterns from coss particles + +### Key patterns + +Card skeleton composition (match the final layout geometry): + +```tsx +
+ +
+ + +
+ +
+``` + +Shape matching: mirror final text/image/button geometry to reduce content shift. +State handoff: remove skeleton immediately when data is ready to avoid double-render. + +### More examples + +- skeleton-only layout: `p-skeleton-2` +- full loading-to-loaded flow: `p-skeleton-1` + +## Common pitfalls + +- Mismatch between skeleton layout and final content layout causing jarring swap. +- Leaving skeleton visible after load completion due missing state transition. +- Using skeleton for very short operations where spinner/text is clearer. + +## Useful particle references + +See `p-skeleton-1` (full loading flow) and `p-skeleton-2` (skeleton-only layout). diff --git a/.agents/skills/coss/references/primitives/slider.md b/.agents/skills/coss/references/primitives/slider.md new file mode 100644 index 0000000..65d2da0 --- /dev/null +++ b/.agents/skills/coss/references/primitives/slider.md @@ -0,0 +1,69 @@ +# coss Slider + +## When to use + +- Continuous or ranged numeric tuning interactions. +- Volume/brightness/threshold controls with immediate feedback. + +## Install + +```bash +npx shadcn@latest add @coss/slider +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Slider, SliderValue } from "@/components/ui/slider" +``` + +## Minimal pattern + +```tsx + +``` + +## Patterns from coss particles + +### Key patterns + +Slider with label and live value display: + +```tsx +
+
+ + +
+ +
+``` + +Range slider (two thumbs): + +```tsx + +``` + +### More examples + +See `p-slider-1` through `p-slider-5` for label, range, vertical, and form integration patterns. + +## Common pitfalls + +- Using slider for discrete option labels where select/radio is clearer. +- Not exposing current value context in nearby UI text when needed. +- Confusing single-value vs range value shapes in controlled mode. + +## Useful particle references + +- with label and value: `p-slider-2` +- range slider: `p-slider-3` +- vertical: `p-slider-4` +- form integration: `p-slider-5` diff --git a/.agents/skills/coss/references/primitives/spinner.md b/.agents/skills/coss/references/primitives/spinner.md new file mode 100644 index 0000000..1566c15 --- /dev/null +++ b/.agents/skills/coss/references/primitives/spinner.md @@ -0,0 +1,73 @@ +# coss Spinner + +## When to use + +- Indeterminate loading indicator for ongoing work. +- Inline pending state in buttons, forms, and async panels. + +## Install + +```bash +npx shadcn@latest add @coss/spinner +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { Spinner } from "@/components/ui/spinner" +``` + +## Minimal pattern + +```tsx +
+ + Loading data… +
+``` + +## Patterns from coss particles + +### Key patterns + +Spinner inside a button: + +```tsx + +``` + +Standalone spinner with label: + +```tsx +
+ + Fetching data... +
+``` + +Prefer button built-in loading UI where available over ad-hoc spinner wrappers. + +### More examples + +See `p-button-18` and `p-input-12` for spinner-in-button and spinner-in-input-group patterns. + +## Common pitfalls + +- Using spinner without accessible label/context for screen readers. +- Showing spinner with no cancel/retry pathway in long-running operations. +- Using spinner when determinate progress value is available. + +## Useful particle references + +- input group: `p-input-12` +- button: `p-button-18` +- cross-primitive pending states: `p-button-25`, `p-autocomplete-12`, `p-toast-3` diff --git a/.agents/skills/coss/references/primitives/switch.md b/.agents/skills/coss/references/primitives/switch.md new file mode 100644 index 0000000..55db1b4 --- /dev/null +++ b/.agents/skills/coss/references/primitives/switch.md @@ -0,0 +1,86 @@ +# coss Switch + +## When to use + +- Binary preference toggles in settings flows. +- Immediate on/off state controls with explicit labels. + +## When NOT to use + +- If the control is a pressable command (not a preference) -> use Toggle instead. +- If you need grouped toggle selection -> use ToggleGroup instead. +- If it's a form agreement (terms/conditions) -> use Checkbox instead. + +## Install + +```bash +npx shadcn@latest add @coss/switch +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Switch } from "@/components/ui/switch" +``` + +## Minimal pattern + +```tsx + +``` + +## Patterns from coss particles + +### Key patterns + +Switch with description text (use `id`/`htmlFor` when additional content exists): + +```tsx +const id = useId() + +
+
+ +

+ Receive updates about new products and features. +

+
+ +
+``` + +Disabled switch: + +```tsx + +``` + +### More examples + +- disabled: `p-switch-2` +- with description: `p-switch-3` +- customizing size: `p-switch-6` +- card style: `p-switch-4` +- form integration: `p-switch-5` + +## Common pitfalls + +- Using switch for multi-option selection that should use radio/toggle-group. +- Omitting visible label or explicit `aria-label` for icon-only switch controls. +- Treating switch as form value without verifying checked-state wiring. + +## Useful particle references + +See `p-switch-1` through `p-switch-6` for label, disabled, description, card, form, and size patterns. diff --git a/.agents/skills/coss/references/primitives/table.md b/.agents/skills/coss/references/primitives/table.md new file mode 100644 index 0000000..8d9a5a7 --- /dev/null +++ b/.agents/skills/coss/references/primitives/table.md @@ -0,0 +1,81 @@ +# coss Table + +## When to use + +- Structured tabular datasets. +- Sortable/filterable row and column displays. + +## Install + +```bash +npx shadcn@latest add @coss/table +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for basic table usage. +# For interactive data tables, add TanStack Table: +npm install @tanstack/react-table +``` + +## Canonical imports + +```tsx +import { + Table, + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +``` + +## Minimal pattern + +```tsx + + + + Name + + + + + Ada Lovelace + + +
+``` + +## Patterns from coss particles + +- **Semantic baseline**: start with `TableHeader`/`TableBody`/`TableRow`/`TableHead`/`TableCell`, then add `TableCaption` and `TableFooter` as needed. +- **Card variant**: set `variant="card"` on `Table` for rounded, card-like rows and separated borders (`p-table-5`). Combine with `Frame` for app-surface framing (`p-table-2`), with `CardFrame` for static tables in a card shell (`p-table-7`), or with TanStack inside `CardFrame` for selection (`p-table-6`) or sorting and pagination (`p-table-8`). +- **Status-rich rows**: combine `Badge` and decorative dots/icons for state columns while keeping text primary. +- **Interactive data grids**: pair coss table parts with TanStack Table (`flexRender`, row models, selection state) for sorting/pagination/selection. +- **No-results state**: always render an explicit empty-state row with `colSpan` matching visible columns. +- **Fixed layout control**: use `className="table-fixed"` and column width styles when predictable column sizing is required. + +## Common pitfalls + +- Assuming `Table` itself provides sorting/filter/pagination state; these come from your data layer (for example TanStack Table). +- Mixing header/body cell semantics (`TableHead` in body rows or `TableCell` in headers). +- Forgetting to align `colSpan` with actual visible columns in footer/empty rows. +- Using table patterns where card/list layouts are more suitable on small screens without responsive handling. +- Omitting `aria-label` for row-selection checkboxes in interactive tables. + +## Useful particle references + +- basic semantic table with caption/footer: `p-table-1` +- `Frame` + card variant: `p-table-2` +- TanStack + checkboxes + `Frame`: `p-table-3` +- TanStack sorting + pagination + `Frame`: `p-table-4` +- card variant only (no shell): `p-table-5` +- `CardFrame` + static table: `p-table-7` +- `CardFrame` + TanStack + checkboxes: `p-table-6` +- `CardFrame` + TanStack + sort + pagination: `p-table-8` + diff --git a/.agents/skills/coss/references/primitives/tabs.md b/.agents/skills/coss/references/primitives/tabs.md new file mode 100644 index 0000000..769e700 --- /dev/null +++ b/.agents/skills/coss/references/primitives/tabs.md @@ -0,0 +1,86 @@ +# coss Tabs + +## When to use + +- Mutually exclusive content panels in one region. +- Settings/detail screens split into scoped views. + +## Install + +```bash +npx shadcn@latest add @coss/tabs +``` + +Manual deps from docs: + +```bash +# No extra runtime dependency required for this primitive. +``` + +## Canonical imports + +```tsx +import { Tabs, TabsList, TabsPanel, TabsTab } from "@/components/ui/tabs" +``` + +## Minimal pattern + +```tsx + + + Tab 1 + Tab 2 + Tab 3 + + Tab 1 content + Tab 2 content + Tab 3 content + +``` + +## Patterns from coss particles + +### Key patterns + +Controlled tabs with external state: + +```tsx +const [value, setValue] = useState("tab-1") + + + + Tab 1 + Tab 2 + + Content 1 + Content 2 + +``` + +Underline variant: + +```tsx + + + Tab 1 + Tab 2 + + ... + +``` + +### More examples + +- underline variant: `p-tabs-2` +- vertical orientation: `p-tabs-3` +- underline with vertical orientation: `p-tabs-4` + +## Common pitfalls + +- Mismatching `TabsTab value` and `TabsPanel value` pairs. +- Using tabs for workflows that require route-level navigation instead. +- Mounting expensive panel content without considering visibility/performance. + +## Useful particle references + +See `p-tabs-1` through `p-tabs-4` for variants and orientations. Related: `p-toolbar-1`, `p-card-1`. diff --git a/.agents/skills/coss/references/primitives/textarea.md b/.agents/skills/coss/references/primitives/textarea.md new file mode 100644 index 0000000..b614dc2 --- /dev/null +++ b/.agents/skills/coss/references/primitives/textarea.md @@ -0,0 +1,71 @@ +# coss Textarea + +## When to use + +- Multi-line text entry (notes, feedback, descriptions). +- Comment/message inputs where text length is variable. + +## Install + +```bash +npx shadcn@latest add @coss/textarea +``` + +Manual deps from docs: + +```bash +npm install @base-ui/react +``` + +## Canonical imports + +```tsx +import { Textarea } from "@/components/ui/textarea" +``` + +## Minimal pattern + +```tsx +