@hex-core/components 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.ts +3556 -0
- package/dist/accordion.d.ts +4 -13
- package/dist/alert-dialog.d.ts +11 -34
- package/dist/alert.d.ts +4 -17
- package/dist/aspect-ratio.d.ts +1 -7
- package/dist/attachment.d.ts +4 -0
- package/dist/attachment.js +157 -0
- package/dist/attachment.js.map +1 -0
- package/dist/avatar.d.ts +3 -11
- package/dist/badge.d.ts +3 -22
- package/dist/breadcrumb.d.ts +7 -27
- package/dist/button.d.ts +3 -13
- package/dist/calendar.d.ts +1 -17
- package/dist/card.d.ts +6 -16
- package/dist/checkbox.d.ts +2 -11
- package/dist/citation.d.ts +2 -0
- package/dist/citation.js +70 -0
- package/dist/citation.js.map +1 -0
- package/dist/cluster.d.ts +3 -34
- package/dist/code-block-copy.d.ts +2 -0
- package/dist/code-block-copy.js +108 -0
- package/dist/code-block-copy.js.map +1 -0
- package/dist/code-block.d.ts +3 -0
- package/dist/code-block.js +90 -0
- package/dist/code-block.js.map +1 -0
- package/dist/collapsible.d.ts +3 -11
- package/dist/color-picker.d.ts +2 -44
- package/dist/combobox.d.ts +3 -45
- package/dist/command.d.ts +9 -111
- package/dist/composer.d.ts +2 -0
- package/dist/composer.js +75 -0
- package/dist/composer.js.map +1 -0
- package/dist/container.d.ts +3 -41
- package/dist/context-menu.d.ts +12 -37
- package/dist/data-table.d.ts +2 -33
- package/dist/date-picker.d.ts +2 -43
- package/dist/dialog.d.ts +11 -46
- package/dist/drawer.d.ts +10 -41
- package/dist/dropdown-menu.d.ts +13 -39
- package/dist/dropzone.d.ts +3 -54
- package/dist/dropzone.js +46 -44
- package/dist/dropzone.js.map +1 -1
- package/dist/empty.d.ts +3 -0
- package/dist/empty.js +94 -0
- package/dist/empty.js.map +1 -0
- package/dist/error-state.d.ts +3 -0
- package/dist/error-state.js +67 -0
- package/dist/error-state.js.map +1 -0
- package/dist/file-tree.d.ts +3 -53
- package/dist/form.d.ts +8 -45
- package/dist/grid.d.ts +3 -50
- package/dist/hover-card.d.ts +3 -11
- package/dist/index.d.ts +325 -179
- package/dist/index.js +1592 -122
- package/dist/index.js.map +1 -1
- package/dist/input-otp.d.ts +5 -19
- package/dist/input.d.ts +2 -6
- package/dist/label.d.ts +2 -11
- package/dist/loading-indicator.d.ts +3 -0
- package/dist/loading-indicator.js +64 -0
- package/dist/loading-indicator.js.map +1 -0
- package/dist/loading.d.ts +3 -0
- package/dist/loading.js +80 -0
- package/dist/loading.js.map +1 -0
- package/dist/markdown.d.ts +2 -0
- package/dist/markdown.js +28 -0
- package/dist/markdown.js.map +1 -0
- package/dist/menubar.d.ts +11 -35
- package/dist/message-actions.d.ts +2 -0
- package/dist/message-actions.js +28 -0
- package/dist/message-actions.js.map +1 -0
- package/dist/message-list.d.ts +2 -0
- package/dist/message-list.js +49 -0
- package/dist/message-list.js.map +1 -0
- package/dist/message.d.ts +3 -0
- package/dist/message.js +35 -0
- package/dist/message.js.map +1 -0
- package/dist/multi-combobox.d.ts +3 -51
- package/dist/navigation-menu.d.ts +9 -23
- package/dist/pagination.d.ts +7 -40
- package/dist/popover.d.ts +4 -13
- package/dist/progress.d.ts +1 -10
- package/dist/radio-group.d.ts +2 -9
- package/dist/reasoning.d.ts +2 -0
- package/dist/reasoning.js +90 -0
- package/dist/reasoning.js.map +1 -0
- package/dist/resizable.d.ts +3 -28
- package/dist/schemas.d.ts +79 -121
- package/dist/schemas.js +1649 -1
- package/dist/schemas.js.map +1 -1
- package/dist/scroll-area.d.ts +3 -18
- package/dist/select.d.ts +8 -21
- package/dist/separator.d.ts +2 -11
- package/dist/sheet.d.ts +10 -39
- package/dist/sidebar.d.ts +8 -75
- package/dist/skeleton.d.ts +1 -11
- package/dist/slider.d.ts +2 -20
- package/dist/sonner.d.ts +2 -14
- package/dist/spacer.d.ts +3 -38
- package/dist/stack.d.ts +3 -34
- package/dist/stepper.d.ts +4 -48
- package/dist/suggestion.d.ts +2 -0
- package/dist/suggestion.js +55 -0
- package/dist/suggestion.js.map +1 -0
- package/dist/switch.d.ts +2 -11
- package/dist/table.d.ts +8 -24
- package/dist/tabs.d.ts +4 -13
- package/dist/tag.d.ts +3 -0
- package/dist/tag.js +107 -0
- package/dist/tag.js.map +1 -0
- package/dist/textarea.d.ts +2 -10
- package/dist/time-picker.d.ts +2 -34
- package/dist/timeline.d.ts +4 -42
- package/dist/toggle-group.d.ts +2 -17
- package/dist/toggle.d.ts +2 -19
- package/dist/tool-call.d.ts +2 -0
- package/dist/tool-call.js +133 -0
- package/dist/tool-call.js.map +1 -0
- package/dist/toolbar.d.ts +8 -0
- package/dist/toolbar.js +120 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/tooltip.d.ts +4 -13
- package/dist/tree.d.ts +3 -0
- package/dist/tree.js +275 -0
- package/dist/tree.js.map +1 -0
- package/package.json +5 -1
- package/dist/button-variants-Bx6gCUFp.d.ts +0 -19
package/dist/schemas.js
CHANGED
|
@@ -4638,6 +4638,1654 @@ var sidebarSchema = {
|
|
|
4638
4638
|
tags: ["sidebar", "navigation", "app-shell", "layout"]
|
|
4639
4639
|
};
|
|
4640
4640
|
|
|
4641
|
-
|
|
4641
|
+
// src/primitives/empty/empty.schema.ts
|
|
4642
|
+
var emptySchema = {
|
|
4643
|
+
name: "empty",
|
|
4644
|
+
displayName: "Empty",
|
|
4645
|
+
description: "A zero-state surface for lists, dashboards, and search results with no content. Explains why the slot is empty + provides a call-to-action.",
|
|
4646
|
+
category: "primitive",
|
|
4647
|
+
subcategory: "feedback",
|
|
4648
|
+
props: [
|
|
4649
|
+
{
|
|
4650
|
+
name: "icon",
|
|
4651
|
+
type: "ReactNode",
|
|
4652
|
+
required: false,
|
|
4653
|
+
description: "Icon (typically an <svg>) rendered in a circular muted container above the title."
|
|
4654
|
+
},
|
|
4655
|
+
{
|
|
4656
|
+
name: "title",
|
|
4657
|
+
type: "ReactNode",
|
|
4658
|
+
required: true,
|
|
4659
|
+
description: "Heading copy. Becomes the region's accessible name via aria-labelledby."
|
|
4660
|
+
},
|
|
4661
|
+
{
|
|
4662
|
+
name: "description",
|
|
4663
|
+
type: "ReactNode",
|
|
4664
|
+
required: false,
|
|
4665
|
+
description: "Supporting copy that explains why the slot is empty + what to do next."
|
|
4666
|
+
},
|
|
4667
|
+
{
|
|
4668
|
+
name: "action",
|
|
4669
|
+
type: "ReactNode",
|
|
4670
|
+
required: false,
|
|
4671
|
+
description: "Call-to-action \u2014 typically a <Button> that creates the missing record."
|
|
4672
|
+
},
|
|
4673
|
+
{
|
|
4674
|
+
name: "titleAs",
|
|
4675
|
+
type: "enum",
|
|
4676
|
+
required: false,
|
|
4677
|
+
default: "h3",
|
|
4678
|
+
description: "Heading level for the title. Pick to match surrounding heading hierarchy.",
|
|
4679
|
+
enumValues: ["h2", "h3", "h4", "h5", "h6", "p"]
|
|
4680
|
+
},
|
|
4681
|
+
{
|
|
4682
|
+
name: "size",
|
|
4683
|
+
type: "enum",
|
|
4684
|
+
required: false,
|
|
4685
|
+
default: "default",
|
|
4686
|
+
description: "Visual scale.",
|
|
4687
|
+
enumValues: ["sm", "default", "lg"]
|
|
4688
|
+
}
|
|
4689
|
+
],
|
|
4690
|
+
variants: [
|
|
4691
|
+
{
|
|
4692
|
+
name: "size",
|
|
4693
|
+
description: "Vertical density of the empty-state surface.",
|
|
4694
|
+
values: [
|
|
4695
|
+
{
|
|
4696
|
+
value: "sm",
|
|
4697
|
+
description: "Compact \u2014 fits inside a card / panel.",
|
|
4698
|
+
useWhen: "the empty surface is one of several stacked pieces of content; full-bleed would dominate"
|
|
4699
|
+
},
|
|
4700
|
+
{
|
|
4701
|
+
value: "default",
|
|
4702
|
+
description: "Standard \u2014 for table / list / dashboard widget empty states.",
|
|
4703
|
+
useWhen: "default everywhere; the only size you need on most surfaces"
|
|
4704
|
+
},
|
|
4705
|
+
{
|
|
4706
|
+
value: "lg",
|
|
4707
|
+
description: "Large \u2014 for full-page empty states (no items at all yet).",
|
|
4708
|
+
useWhen: "the empty surface owns the entire page \u2014 onboarding, first-run, no records yet"
|
|
4709
|
+
}
|
|
4710
|
+
],
|
|
4711
|
+
default: "default"
|
|
4712
|
+
}
|
|
4713
|
+
],
|
|
4714
|
+
slots: [
|
|
4715
|
+
{
|
|
4716
|
+
name: "icon",
|
|
4717
|
+
description: "Icon rendered in the circular muted container above the title.",
|
|
4718
|
+
required: false,
|
|
4719
|
+
acceptedTypes: ["ReactNode"]
|
|
4720
|
+
},
|
|
4721
|
+
{
|
|
4722
|
+
name: "action",
|
|
4723
|
+
description: "Call-to-action button under the description.",
|
|
4724
|
+
required: false,
|
|
4725
|
+
acceptedTypes: ["ReactNode"]
|
|
4726
|
+
}
|
|
4727
|
+
],
|
|
4728
|
+
dependencies: {
|
|
4729
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
4730
|
+
internal: [],
|
|
4731
|
+
peer: ["react", "react-dom"]
|
|
4732
|
+
},
|
|
4733
|
+
tokensUsed: ["muted", "muted-foreground", "foreground", "border"],
|
|
4734
|
+
examples: [
|
|
4735
|
+
{
|
|
4736
|
+
title: "List zero-state",
|
|
4737
|
+
description: "Inbox with no messages \u2014 icon + title + description + compose button",
|
|
4738
|
+
code: '<Empty\n icon={<InboxIcon />}\n title="No messages yet"\n description="When someone sends a message, it shows up here."\n action={<Button>Compose</Button>}\n/>',
|
|
4739
|
+
composition: ["zero-state", "list", "with-action"]
|
|
4740
|
+
},
|
|
4741
|
+
{
|
|
4742
|
+
title: "Search zero-state",
|
|
4743
|
+
description: "Search returned zero results \u2014 show the query + suggest a reset",
|
|
4744
|
+
code: '<Empty\n icon={<SearchIcon />}\n title="No results found"\n description={`No items match "${query}". Try a different search.`}\n action={<Button variant="secondary" onClick={onReset}>Clear search</Button>}\n size="sm"\n/>',
|
|
4745
|
+
composition: ["zero-state", "search"]
|
|
4746
|
+
},
|
|
4747
|
+
{
|
|
4748
|
+
title: "Onboarding",
|
|
4749
|
+
description: "Full-page first-run state",
|
|
4750
|
+
code: '<Empty\n icon={<SparklesIcon />}\n title="Welcome to your dashboard"\n description="Create your first project to get started."\n action={<Button size="lg">Create project</Button>}\n size="lg"\n titleAs="h1"\n/>',
|
|
4751
|
+
composition: ["zero-state", "onboarding", "first-run"]
|
|
4752
|
+
}
|
|
4753
|
+
],
|
|
4754
|
+
ai: {
|
|
4755
|
+
whenToUse: "Use for lists, search results, or dashboards where the request returned successfully but there's nothing to show. Explain why and what to do \u2014 the title names the absence, the description sets context, the action gives a way out.",
|
|
4756
|
+
whenNotToUse: "Don't use while data is still loading (use Loading). Don't use when something failed (use ErrorState). Don't use for inline form-field 'no options' messages (use Combobox's empty slot or Form's helper text).",
|
|
4757
|
+
commonMistakes: [
|
|
4758
|
+
"Using Empty without an action \u2014 leaves the user stuck",
|
|
4759
|
+
"Long descriptions \u2014 keep to one sentence; this is a state surface, not documentation",
|
|
4760
|
+
"Forgetting titleAs when the page already has an h2 above (heading hierarchy break)"
|
|
4761
|
+
],
|
|
4762
|
+
antiPatterns: [
|
|
4763
|
+
{
|
|
4764
|
+
mistake: "Using Empty while data is still being fetched",
|
|
4765
|
+
insteadUse: "loading",
|
|
4766
|
+
why: "Empty announces 'no items exist'; Loading announces 'fetching items.' Showing Empty during the fetch flashes a misleading 'no results' state then snaps to real data on resolution."
|
|
4767
|
+
},
|
|
4768
|
+
{
|
|
4769
|
+
mistake: "Using Empty to display a fetch failure",
|
|
4770
|
+
insteadUse: "error-state",
|
|
4771
|
+
why: "ErrorState carries role='alert' so screen readers announce the failure on first render, AND it ships a retry affordance. Empty is silent on retry semantics."
|
|
4772
|
+
},
|
|
4773
|
+
{
|
|
4774
|
+
mistake: "Using Empty as an inline 'no matching options' notice inside a Combobox or Select",
|
|
4775
|
+
insteadUse: "combobox",
|
|
4776
|
+
why: "Combobox + Command already render a sub-AA-conformant empty state inside the listbox. Using Empty there leaks the page-level zero-state language into a focused interaction."
|
|
4777
|
+
}
|
|
4778
|
+
],
|
|
4779
|
+
relatedComponents: ["loading", "error-state", "alert", "card", "skeleton"],
|
|
4780
|
+
accessibilityNotes: "Wrapped in a region landmark labeled by the title (aria-labelledby + auto-generated id). Icon is decorative (aria-hidden). Pair the action with a Button that has a visible label.",
|
|
4781
|
+
tokenBudget: 350
|
|
4782
|
+
},
|
|
4783
|
+
tags: ["empty", "zero-state", "feedback", "list", "dashboard"]
|
|
4784
|
+
};
|
|
4785
|
+
|
|
4786
|
+
// src/primitives/loading/loading.schema.ts
|
|
4787
|
+
var loadingSchema = {
|
|
4788
|
+
name: "loading",
|
|
4789
|
+
displayName: "Loading",
|
|
4790
|
+
description: "A composed loading-placeholder pattern for lists, cards, and stacks. Skeleton is the atom; Loading is the canonical molecule most surfaces want.",
|
|
4791
|
+
category: "primitive",
|
|
4792
|
+
subcategory: "feedback",
|
|
4793
|
+
props: [
|
|
4794
|
+
{
|
|
4795
|
+
name: "variant",
|
|
4796
|
+
type: "enum",
|
|
4797
|
+
required: false,
|
|
4798
|
+
default: "list",
|
|
4799
|
+
description: "Layout family \u2014 picks the row shape.",
|
|
4800
|
+
enumValues: ["list", "card", "stack"]
|
|
4801
|
+
},
|
|
4802
|
+
{
|
|
4803
|
+
name: "rows",
|
|
4804
|
+
type: "number",
|
|
4805
|
+
required: false,
|
|
4806
|
+
default: 3,
|
|
4807
|
+
description: "Number of placeholder rows to render. Each row may contain multiple Skeleton elements depending on variant \u2014 list emits 1 per row; card emits 3 (heading + 2 body lines); stack emits 3 (avatar + 2 text lines)."
|
|
4808
|
+
},
|
|
4809
|
+
{
|
|
4810
|
+
name: "label",
|
|
4811
|
+
type: "string",
|
|
4812
|
+
required: false,
|
|
4813
|
+
default: "Loading\u2026",
|
|
4814
|
+
description: "sr-only label announced by screen readers via aria-live polite."
|
|
4815
|
+
},
|
|
4816
|
+
{ name: "className", type: "string", required: false, description: "Additional CSS classes" }
|
|
4817
|
+
],
|
|
4818
|
+
variants: [
|
|
4819
|
+
{
|
|
4820
|
+
name: "variant",
|
|
4821
|
+
description: "Layout family for the placeholder rows.",
|
|
4822
|
+
values: [
|
|
4823
|
+
{
|
|
4824
|
+
value: "list",
|
|
4825
|
+
description: "Single full-width line per row \u2014 table / list / sidebar nav loaders.",
|
|
4826
|
+
useWhen: "the surface being filled is a flat list of equally-shaped rows"
|
|
4827
|
+
},
|
|
4828
|
+
{
|
|
4829
|
+
value: "card",
|
|
4830
|
+
description: "Heading + two body lines inside a bordered card surface.",
|
|
4831
|
+
useWhen: "loading a Card-shaped widget (settings panel, dashboard tile)"
|
|
4832
|
+
},
|
|
4833
|
+
{
|
|
4834
|
+
value: "stack",
|
|
4835
|
+
description: "Avatar + two-line text block per row \u2014 message / contact / search-result loaders.",
|
|
4836
|
+
useWhen: "loading a list of avatar-paired items (chat history, contact list, comment thread)"
|
|
4837
|
+
}
|
|
4838
|
+
],
|
|
4839
|
+
default: "list"
|
|
4840
|
+
}
|
|
4841
|
+
],
|
|
4842
|
+
slots: [],
|
|
4843
|
+
dependencies: {
|
|
4844
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
4845
|
+
internal: ["skeleton"],
|
|
4846
|
+
peer: ["react", "react-dom"]
|
|
4847
|
+
},
|
|
4848
|
+
tokensUsed: ["muted", "card", "border"],
|
|
4849
|
+
examples: [
|
|
4850
|
+
{
|
|
4851
|
+
title: "List loader",
|
|
4852
|
+
description: "5 placeholder rows for a dashboard table",
|
|
4853
|
+
code: '<Loading rows={5} variant="list" />',
|
|
4854
|
+
composition: ["loading", "table"]
|
|
4855
|
+
},
|
|
4856
|
+
{
|
|
4857
|
+
title: "Card loader",
|
|
4858
|
+
description: "Card-shaped placeholder with heading + 2 body lines",
|
|
4859
|
+
code: '<Loading variant="card" />',
|
|
4860
|
+
composition: ["loading", "card", "widget"]
|
|
4861
|
+
},
|
|
4862
|
+
{
|
|
4863
|
+
title: "Stack loader (chat / contacts)",
|
|
4864
|
+
description: "Avatar + 2-line text rows",
|
|
4865
|
+
code: '<Loading rows={4} variant="stack" />',
|
|
4866
|
+
composition: ["loading", "stack", "chat"]
|
|
4867
|
+
}
|
|
4868
|
+
],
|
|
4869
|
+
ai: {
|
|
4870
|
+
whenToUse: "Use when fetching data for a list, card, or avatar-paired stack and you want pre-arranged placeholders. Pair the rows count with how many items you expect \u2014 avoid 10-row loaders for 2-item results (jarring on resolve).",
|
|
4871
|
+
whenNotToUse: "Don't use for a single shaped placeholder (use Skeleton). Don't use when the wait has a measurable percent-done (use Progress). Don't use for full-page transitions where a top progress bar is more informative.",
|
|
4872
|
+
commonMistakes: [
|
|
4873
|
+
"Forgetting `aria-live` would prevent screen-reader announcement \u2014 Loading already wires it; don't override role",
|
|
4874
|
+
"Using too many rows \u2014 flashes a heavy 'something's wrong' impression",
|
|
4875
|
+
"Applying Loading inside a Card AND also wrapping in another Card \u2014 double-bordered shimmer"
|
|
4876
|
+
],
|
|
4877
|
+
antiPatterns: [
|
|
4878
|
+
{
|
|
4879
|
+
mistake: "Using Loading for a single shaped placeholder (avatar circle, heading line, image rectangle)",
|
|
4880
|
+
insteadUse: "skeleton",
|
|
4881
|
+
why: "Skeleton is the atom; render exactly one Skeleton with the right size class. Loading composes Skeletons for canonical multi-row patterns \u2014 no value if you only need one shape."
|
|
4882
|
+
},
|
|
4883
|
+
{
|
|
4884
|
+
mistake: "Using Loading when the wait has a measurable percent (file upload, batch import)",
|
|
4885
|
+
insteadUse: "progress",
|
|
4886
|
+
why: "Progress's `value` prop announces actual completion to assistive tech. Loading is for indeterminate waits."
|
|
4887
|
+
},
|
|
4888
|
+
{
|
|
4889
|
+
mistake: "Using Loading for a full-page route transition where the user just clicked a link",
|
|
4890
|
+
insteadUse: "skeleton",
|
|
4891
|
+
why: "Compose page-shaped Skeletons via `generateStaticParams` + Suspense; full-page Loading flashes 'work is happening here' while the right answer is 'show the page shell instantly.'"
|
|
4892
|
+
}
|
|
4893
|
+
],
|
|
4894
|
+
relatedComponents: ["skeleton", "progress", "empty", "error-state"],
|
|
4895
|
+
accessibilityNotes: "role='status' + aria-live='polite' + sr-only label, so screen readers announce on first render. Caller doesn't need to wire any of this.",
|
|
4896
|
+
tokenBudget: 300
|
|
4897
|
+
},
|
|
4898
|
+
tags: ["loading", "skeleton", "placeholder", "feedback"]
|
|
4899
|
+
};
|
|
4900
|
+
|
|
4901
|
+
// src/primitives/error-state/error-state.schema.ts
|
|
4902
|
+
var errorStateSchema = {
|
|
4903
|
+
name: "error-state",
|
|
4904
|
+
displayName: "ErrorState",
|
|
4905
|
+
description: "A surface for failed-fetch / failed-action states. Visually mirrors Empty but with destructive bias and an optional retry button. Mounts with role='alert' so screen readers announce on first render.",
|
|
4906
|
+
category: "primitive",
|
|
4907
|
+
subcategory: "feedback",
|
|
4908
|
+
props: [
|
|
4909
|
+
{ name: "icon", type: "ReactNode", required: false, description: "Icon (typically an alert / x-circle SVG)." },
|
|
4910
|
+
{ name: "title", type: "ReactNode", required: false, default: "Something went wrong", description: "Heading copy. Falls back to a generic message." },
|
|
4911
|
+
{ name: "message", type: "ReactNode", required: true, description: "Body copy explaining what failed and (optionally) what the user can do." },
|
|
4912
|
+
{ name: "action", type: "ReactNode", required: false, description: "Call-to-action slot \u2014 typically a <Button onClick={refetch}>Retry</Button>. Consumer controls the button variant, loading state, and asChild composition." },
|
|
4913
|
+
{
|
|
4914
|
+
name: "variant",
|
|
4915
|
+
type: "enum",
|
|
4916
|
+
required: false,
|
|
4917
|
+
default: "default",
|
|
4918
|
+
description: "Visual tone \u2014 destructive draws more attention, default is calmer.",
|
|
4919
|
+
enumValues: ["default", "destructive"]
|
|
4920
|
+
}
|
|
4921
|
+
],
|
|
4922
|
+
variants: [
|
|
4923
|
+
{
|
|
4924
|
+
name: "variant",
|
|
4925
|
+
description: "Tone of the surface.",
|
|
4926
|
+
values: [
|
|
4927
|
+
{
|
|
4928
|
+
value: "default",
|
|
4929
|
+
description: "Muted neutral surface \u2014 for recoverable / transient failures.",
|
|
4930
|
+
useWhen: "the failure is recoverable and you don't want to alarm: 'temporarily unavailable,' 'try again in a moment'"
|
|
4931
|
+
},
|
|
4932
|
+
{
|
|
4933
|
+
value: "destructive",
|
|
4934
|
+
description: "Tinted destructive surface \u2014 for hard / unrecoverable failures.",
|
|
4935
|
+
useWhen: "the failure is significant: data lost, permission denied, server error 5xx"
|
|
4936
|
+
}
|
|
4937
|
+
],
|
|
4938
|
+
default: "default"
|
|
4939
|
+
}
|
|
4940
|
+
],
|
|
4941
|
+
slots: [
|
|
4942
|
+
{
|
|
4943
|
+
name: "icon",
|
|
4944
|
+
description: "Icon rendered in the circular tinted container.",
|
|
4945
|
+
required: false,
|
|
4946
|
+
acceptedTypes: ["ReactNode"]
|
|
4947
|
+
},
|
|
4948
|
+
{
|
|
4949
|
+
name: "action",
|
|
4950
|
+
description: "Call-to-action \u2014 typically a Button. Consumer composes the button variant + click handler.",
|
|
4951
|
+
required: false,
|
|
4952
|
+
acceptedTypes: ["ReactNode"]
|
|
4953
|
+
}
|
|
4954
|
+
],
|
|
4955
|
+
dependencies: {
|
|
4956
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
4957
|
+
internal: [],
|
|
4958
|
+
peer: ["react", "react-dom"]
|
|
4959
|
+
},
|
|
4960
|
+
tokensUsed: ["destructive", "muted", "muted-foreground", "foreground", "border"],
|
|
4961
|
+
examples: [
|
|
4962
|
+
{
|
|
4963
|
+
title: "Fetch failure with retry",
|
|
4964
|
+
description: "Couldn't load messages \u2014 show the error + a Retry button in the action slot",
|
|
4965
|
+
code: `<ErrorState
|
|
4966
|
+
icon={<AlertCircleIcon />}
|
|
4967
|
+
title="Couldn't load messages"
|
|
4968
|
+
message="The server didn't respond. Check your connection and try again."
|
|
4969
|
+
action={<Button onClick={() => refetch()}>Retry</Button>}
|
|
4970
|
+
/>`,
|
|
4971
|
+
composition: ["error-state", "retry", "fetch-failure"]
|
|
4972
|
+
},
|
|
4973
|
+
{
|
|
4974
|
+
title: "Hard failure (destructive)",
|
|
4975
|
+
description: "Server returned 5xx and we want to alarm",
|
|
4976
|
+
code: `<ErrorState
|
|
4977
|
+
icon={<XCircleIcon />}
|
|
4978
|
+
variant="destructive"
|
|
4979
|
+
title="Sync failed"
|
|
4980
|
+
message="We couldn't save your changes. They're still on this device \u2014 refresh to retry."
|
|
4981
|
+
/>`,
|
|
4982
|
+
composition: ["error-state", "destructive", "sync-failure"]
|
|
4983
|
+
}
|
|
4984
|
+
],
|
|
4985
|
+
ai: {
|
|
4986
|
+
whenToUse: "Use for any failure surface where the user might benefit from context + a retry path. Pair with onRetry whenever the operation is genuinely retryable; omit it when the user has to take a different action (e.g. log in again).",
|
|
4987
|
+
whenNotToUse: "Don't use for inline form-field errors (Form's <FormMessage> handles those). Don't use for blocking destructive confirmations (AlertDialog). Don't use for transient toasts (Sonner / Toaster).",
|
|
4988
|
+
commonMistakes: [
|
|
4989
|
+
"Generic 'Something went wrong' message without context \u2014 at minimum, name what failed",
|
|
4990
|
+
"Using destructive variant for recoverable network blips (cries wolf)",
|
|
4991
|
+
"Wiring the action's click handler to a function that doesn't actually re-trigger the failed operation",
|
|
4992
|
+
"Forgetting the action slot when the failure IS retryable \u2014 leaves the user stuck"
|
|
4993
|
+
],
|
|
4994
|
+
antiPatterns: [
|
|
4995
|
+
{
|
|
4996
|
+
mistake: "Using ErrorState for an inline form-field validation error",
|
|
4997
|
+
insteadUse: "form",
|
|
4998
|
+
why: "Form ships <FormMessage> which is wired to the field's aria-describedby. ErrorState announces page-level failures; using it inline duplicates the role='alert' on every keystroke and confuses assistive tech."
|
|
4999
|
+
},
|
|
5000
|
+
{
|
|
5001
|
+
mistake: "Using ErrorState as a blocking destructive confirmation modal",
|
|
5002
|
+
insteadUse: "alert-dialog",
|
|
5003
|
+
why: "AlertDialog is role='alertdialog' (modal, focus-trapped, dismiss-on-Escape). ErrorState is a passive surface \u2014 no interaction guarantees. If you need the user to confirm before proceeding, use AlertDialog."
|
|
5004
|
+
},
|
|
5005
|
+
{
|
|
5006
|
+
mistake: "Using ErrorState for a transient toast notification",
|
|
5007
|
+
insteadUse: "sonner",
|
|
5008
|
+
why: "Sonner / Toaster auto-dismiss + stack + animate. ErrorState sits in the layout. For 'fire-and-forget failure ack,' use the toaster."
|
|
5009
|
+
}
|
|
5010
|
+
],
|
|
5011
|
+
relatedComponents: ["alert", "alert-dialog", "form", "empty", "loading", "sonner"],
|
|
5012
|
+
accessibilityNotes: "role='alert' so screen readers announce on first render. Icon decorative (aria-hidden). Retry button uses focus-visible ring; pressing it doesn't dismiss the surface \u2014 caller controls that via state.",
|
|
5013
|
+
tokenBudget: 450
|
|
5014
|
+
},
|
|
5015
|
+
tags: ["error", "error-state", "failure", "retry", "feedback"]
|
|
5016
|
+
};
|
|
5017
|
+
|
|
5018
|
+
// src/primitives/tag/tag.schema.ts
|
|
5019
|
+
var tagSchema = {
|
|
5020
|
+
name: "tag",
|
|
5021
|
+
displayName: "Tag",
|
|
5022
|
+
description: "Interactive tag / chip primitive \u2014 Badge with an optional dismiss button. Mirrors Badge's variants so the visual sibling is obvious; ships with a close affordance for filter pills, multi-select selections, draft attachments.",
|
|
5023
|
+
category: "primitive",
|
|
5024
|
+
subcategory: "feedback",
|
|
5025
|
+
props: [
|
|
5026
|
+
{
|
|
5027
|
+
name: "variant",
|
|
5028
|
+
type: "enum",
|
|
5029
|
+
required: false,
|
|
5030
|
+
default: "default",
|
|
5031
|
+
description: "Visual style.",
|
|
5032
|
+
enumValues: ["default", "secondary", "destructive", "outline"]
|
|
5033
|
+
},
|
|
5034
|
+
{
|
|
5035
|
+
name: "icon",
|
|
5036
|
+
type: "ReactNode",
|
|
5037
|
+
required: false,
|
|
5038
|
+
description: "Optional leading icon (sized 12x12 inside the tag)."
|
|
5039
|
+
},
|
|
5040
|
+
{
|
|
5041
|
+
name: "onRemove",
|
|
5042
|
+
type: "function",
|
|
5043
|
+
required: false,
|
|
5044
|
+
description: "Click handler for the close button. When provided, the dismiss \u2715 is rendered. Signature: () => void."
|
|
5045
|
+
},
|
|
5046
|
+
{
|
|
5047
|
+
name: "removeLabel",
|
|
5048
|
+
type: "string",
|
|
5049
|
+
required: false,
|
|
5050
|
+
description: "Override the auto-derived close-button aria-label (default: 'Remove ${children}')."
|
|
5051
|
+
},
|
|
5052
|
+
{ name: "className", type: "string", required: false, description: "Additional CSS classes" }
|
|
5053
|
+
],
|
|
5054
|
+
variants: [
|
|
5055
|
+
{
|
|
5056
|
+
name: "variant",
|
|
5057
|
+
description: "Visual style \u2014 mirrors Badge so users get consistent token use.",
|
|
5058
|
+
values: [
|
|
5059
|
+
{
|
|
5060
|
+
value: "default",
|
|
5061
|
+
description: "Primary-tinted filled tag.",
|
|
5062
|
+
useWhen: "the tag represents an active selected state \u2014 applied filter, picked option"
|
|
5063
|
+
},
|
|
5064
|
+
{
|
|
5065
|
+
value: "secondary",
|
|
5066
|
+
description: "Muted neutral filled tag with a hairline border.",
|
|
5067
|
+
useWhen: "default for everyday tags \u2014 list of selected items, draft attachments, filter pills"
|
|
5068
|
+
},
|
|
5069
|
+
{
|
|
5070
|
+
value: "destructive",
|
|
5071
|
+
description: "Red-tinted filled tag.",
|
|
5072
|
+
useWhen: "the tag represents a flagged / problematic value \u2014 banned user, archived item"
|
|
5073
|
+
},
|
|
5074
|
+
{
|
|
5075
|
+
value: "outline",
|
|
5076
|
+
description: "Bordered transparent tag.",
|
|
5077
|
+
useWhen: "the tag lives on a busy surface and shouldn't add visual fill weight"
|
|
5078
|
+
}
|
|
5079
|
+
],
|
|
5080
|
+
default: "default"
|
|
5081
|
+
}
|
|
5082
|
+
],
|
|
5083
|
+
slots: [
|
|
5084
|
+
{
|
|
5085
|
+
name: "icon",
|
|
5086
|
+
description: "Leading icon (sized 12x12).",
|
|
5087
|
+
required: false,
|
|
5088
|
+
acceptedTypes: ["ReactNode"]
|
|
5089
|
+
},
|
|
5090
|
+
{
|
|
5091
|
+
name: "children",
|
|
5092
|
+
description: "Tag label content (string preferred for auto aria-label derivation).",
|
|
5093
|
+
required: true,
|
|
5094
|
+
acceptedTypes: ["ReactNode"]
|
|
5095
|
+
}
|
|
5096
|
+
],
|
|
5097
|
+
dependencies: {
|
|
5098
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
5099
|
+
internal: [],
|
|
5100
|
+
peer: ["react", "react-dom"]
|
|
5101
|
+
},
|
|
5102
|
+
tokensUsed: ["primary", "primary-foreground", "secondary", "secondary-foreground", "destructive", "destructive-foreground", "foreground", "ring"],
|
|
5103
|
+
examples: [
|
|
5104
|
+
{
|
|
5105
|
+
title: "Filter pill with dismiss",
|
|
5106
|
+
description: "Removable filter chip in a search bar",
|
|
5107
|
+
code: '<Tag variant="secondary" onRemove={() => removeFilter("urgent")}>Urgent</Tag>',
|
|
5108
|
+
composition: ["filter", "search", "removable"]
|
|
5109
|
+
},
|
|
5110
|
+
{
|
|
5111
|
+
title: "Multi-select selections",
|
|
5112
|
+
description: "Stack of selected values with per-tag dismiss",
|
|
5113
|
+
code: '<div className="flex flex-wrap gap-2">\n {selected.map((tag) => (\n <Tag key={tag} variant="secondary" onRemove={() => deselect(tag)}>\n {tag}\n </Tag>\n ))}\n</div>',
|
|
5114
|
+
composition: ["multi-select", "form", "removable"]
|
|
5115
|
+
},
|
|
5116
|
+
{
|
|
5117
|
+
title: "Status tag (non-interactive)",
|
|
5118
|
+
description: "Static tag without dismiss \u2014 equivalent to Badge but uses Tag for consistency",
|
|
5119
|
+
code: '<Tag variant="outline">Beta</Tag>',
|
|
5120
|
+
composition: ["status", "label"]
|
|
5121
|
+
}
|
|
5122
|
+
],
|
|
5123
|
+
ai: {
|
|
5124
|
+
whenToUse: "Use for tokens the user can dismiss: filter pills, multi-select selections, draft attachments. Pair onRemove with a state setter that drops the value from your collection.",
|
|
5125
|
+
whenNotToUse: "Don't use for non-interactive labels (use Badge). Don't use for state-bearing 'click to filter' affordances \u2014 those should be Toggle or ToggleGroup so the active state is announced as 'pressed'/'not pressed' to assistive tech.",
|
|
5126
|
+
commonMistakes: [
|
|
5127
|
+
"Forgetting onRemove on a tag the user CAN dismiss \u2014 leaves the affordance invisible",
|
|
5128
|
+
"Calling it Tag but rendering inside a list of static labels \u2014 confuses Tag (interactive) vs Badge (decorative) semantics",
|
|
5129
|
+
"Wrapping the tag itself in a <button> \u2014 the dismiss button already handles its own click; double-button breaks accessibility"
|
|
5130
|
+
],
|
|
5131
|
+
antiPatterns: [
|
|
5132
|
+
{
|
|
5133
|
+
mistake: "Using Tag without onRemove for a static / decorative label",
|
|
5134
|
+
insteadUse: "badge",
|
|
5135
|
+
why: "Badge is the non-interactive sibling. Tag without onRemove ships an unused close-button container \u2014 wasted DOM and ambiguous semantics. Reach for Badge when there's nothing to dismiss."
|
|
5136
|
+
},
|
|
5137
|
+
{
|
|
5138
|
+
mistake: "Using Tag as a click-to-filter chip (selected vs unselected state)",
|
|
5139
|
+
insteadUse: "toggle",
|
|
5140
|
+
why: "Toggle's role='button' + aria-pressed announces selected state to AT. Tag's close-button doesn't model 'pressed'; consumers end up with non-announced state."
|
|
5141
|
+
},
|
|
5142
|
+
{
|
|
5143
|
+
mistake: "Using Tag for a single-select dropdown's chosen value",
|
|
5144
|
+
insteadUse: "select",
|
|
5145
|
+
why: "Select renders its value text inside the trigger. Tag would duplicate the token and complicate the keyboard nav (focus on Tag's close vs Select's trigger)."
|
|
5146
|
+
}
|
|
5147
|
+
],
|
|
5148
|
+
relatedComponents: ["badge", "toggle", "toggle-group", "multi-combobox"],
|
|
5149
|
+
accessibilityNotes: "Close button gets aria-label derived from children when they're a string ('Remove Urgent'). Override via removeLabel. Tag itself is a span \u2014 wrap in a list (`role='list'` + `role='listitem'`) when rendering N tags as a collection.",
|
|
5150
|
+
tokenBudget: 350
|
|
5151
|
+
},
|
|
5152
|
+
tags: ["tag", "chip", "pill", "filter", "removable", "multi-select"]
|
|
5153
|
+
};
|
|
5154
|
+
|
|
5155
|
+
// src/components/tree/tree.schema.ts
|
|
5156
|
+
var treeSchema = {
|
|
5157
|
+
name: "tree",
|
|
5158
|
+
displayName: "Tree",
|
|
5159
|
+
description: "Generic hierarchical list with roving-tabindex keyboard navigation. Distinct from FileTree (which adds folder/file icon-by-extension logic) \u2014 Tree is content-agnostic for org charts, taxonomy pickers, navigation trees.",
|
|
5160
|
+
category: "component",
|
|
5161
|
+
subcategory: "navigation",
|
|
5162
|
+
props: [
|
|
5163
|
+
{ name: "data", type: "object", required: true, description: "Root nodes (TreeNode[])." },
|
|
5164
|
+
{ name: "defaultExpanded", type: "object", required: false, description: "Initial expanded ids (uncontrolled). string[]." },
|
|
5165
|
+
{ name: "expanded", type: "object", required: false, description: "Controlled expanded ids. string[]." },
|
|
5166
|
+
{ name: "onExpandedChange", type: "function", required: false, description: "Callback (ids: string[]) => void." },
|
|
5167
|
+
{ name: "selected", type: "string", required: false, description: "Controlled selected node id." },
|
|
5168
|
+
{ name: "onSelect", type: "function", required: false, description: "Callback (id: string) => void on activation." },
|
|
5169
|
+
{ name: "aria-label", type: "string", required: true, description: "Accessible name for the tree landmark." },
|
|
5170
|
+
{ name: "className", type: "string", required: false, description: "Additional CSS classes" }
|
|
5171
|
+
],
|
|
5172
|
+
variants: [],
|
|
5173
|
+
slots: [],
|
|
5174
|
+
dependencies: {
|
|
5175
|
+
npm: ["clsx", "tailwind-merge"],
|
|
5176
|
+
internal: [],
|
|
5177
|
+
peer: ["react", "react-dom"]
|
|
5178
|
+
},
|
|
5179
|
+
tokensUsed: ["foreground", "accent", "accent-foreground", "ring"],
|
|
5180
|
+
examples: [
|
|
5181
|
+
{
|
|
5182
|
+
title: "Org chart",
|
|
5183
|
+
description: "Hierarchical staff tree with expand/select",
|
|
5184
|
+
code: '<Tree\n aria-label="Org chart"\n data={[\n { id: "ceo", label: "CEO", children: [\n { id: "cto", label: "CTO", children: [{ id: "eng-lead", label: "Eng Lead" }] },\n { id: "cmo", label: "CMO" },\n ]},\n ]}\n defaultExpanded={["ceo"]}\n onSelect={(id) => console.log(id)}\n/>',
|
|
5185
|
+
composition: ["tree", "navigation", "hierarchy"]
|
|
5186
|
+
},
|
|
5187
|
+
{
|
|
5188
|
+
title: "Taxonomy picker",
|
|
5189
|
+
description: "Categorize a record by picking a leaf in a 3-level taxonomy",
|
|
5190
|
+
code: '<Tree\n aria-label="Category"\n data={taxonomy}\n selected={category}\n onSelect={setCategory}\n/>',
|
|
5191
|
+
composition: ["tree", "form", "picker"]
|
|
5192
|
+
}
|
|
5193
|
+
],
|
|
5194
|
+
ai: {
|
|
5195
|
+
whenToUse: "Use for hierarchical data the user navigates by keyboard or click \u2014 org charts, taxonomy pickers, generic nav trees. Pair with selected + onSelect for picker semantics; pair with onExpandedChange to control expansion externally.",
|
|
5196
|
+
whenNotToUse: "Don't use for filesystem-shaped data \u2014 FileTree adds folder/file icon-by-extension. Don't use for two-level groupings without item-selection \u2014 Accordion is the cleaner abstraction. Don't use for primary site nav (NavigationMenu).",
|
|
5197
|
+
commonMistakes: [
|
|
5198
|
+
"Forgetting aria-label \u2014 required, no fallback",
|
|
5199
|
+
"Confusing data prop (TreeNode[]) with FileTree's nodes prop (FileTreeNode[]) \u2014 same shape but different icon defaults",
|
|
5200
|
+
"Trying to pass selected without onSelect \u2014 controlled-mode without an updater leaves it stuck"
|
|
5201
|
+
],
|
|
5202
|
+
antiPatterns: [
|
|
5203
|
+
{
|
|
5204
|
+
mistake: "Using Tree for filesystem-shaped data (folders + files with icon-by-extension)",
|
|
5205
|
+
insteadUse: "file-tree",
|
|
5206
|
+
why: "FileTree ships file/folder icon defaults + extension-based color hints. Tree is icon-agnostic \u2014 using it for files means hand-wiring icon logic each time."
|
|
5207
|
+
},
|
|
5208
|
+
{
|
|
5209
|
+
mistake: "Using Tree for a two-level grouping (sections that expand to reveal content) without item-selection",
|
|
5210
|
+
insteadUse: "accordion",
|
|
5211
|
+
why: "Accordion is purpose-built for collapse-content-into-sections. Tree's role='tree' implies item-selection semantics that don't fit pure expand/collapse content groups."
|
|
5212
|
+
},
|
|
5213
|
+
{
|
|
5214
|
+
mistake: "Using Tree for primary site / app navigation",
|
|
5215
|
+
insteadUse: "navigation-menu",
|
|
5216
|
+
why: "NavigationMenu ships landmark role + skip links + the right ARIA for primary nav. Tree's tree role is for data, not routing."
|
|
5217
|
+
}
|
|
5218
|
+
],
|
|
5219
|
+
relatedComponents: ["file-tree", "accordion", "navigation-menu"],
|
|
5220
|
+
accessibilityNotes: "role='tree' on root, role='treeitem' per row, aria-expanded on parents, aria-selected on the chosen leaf. Keyboard: \u2191\u2193 move focus, \u2192 expand (or jump to first child if expanded), \u2190 collapse (or jump to parent), Home/End first/last visible row, Enter/Space activate.",
|
|
5221
|
+
tokenBudget: 700
|
|
5222
|
+
},
|
|
5223
|
+
tags: ["tree", "hierarchy", "navigation", "picker", "ARIA-tree"]
|
|
5224
|
+
};
|
|
5225
|
+
|
|
5226
|
+
// src/components/toolbar/toolbar.schema.ts
|
|
5227
|
+
var toolbarSchema = {
|
|
5228
|
+
name: "toolbar",
|
|
5229
|
+
displayName: "Toolbar",
|
|
5230
|
+
description: "A horizontal or vertical group of related controls (buttons, toggles, links, separators) with arrow-key roving focus. Wraps Radix Toolbar so the keyboard semantics are correct out of the box.",
|
|
5231
|
+
category: "component",
|
|
5232
|
+
subcategory: "navigation",
|
|
5233
|
+
props: [
|
|
5234
|
+
{
|
|
5235
|
+
name: "orientation",
|
|
5236
|
+
type: "enum",
|
|
5237
|
+
required: false,
|
|
5238
|
+
default: "horizontal",
|
|
5239
|
+
description: "Layout direction. Drives arrow-key behavior \u2014 \u2190\u2192 for horizontal, \u2191\u2193 for vertical.",
|
|
5240
|
+
enumValues: ["horizontal", "vertical"]
|
|
5241
|
+
},
|
|
5242
|
+
{
|
|
5243
|
+
name: "aria-label",
|
|
5244
|
+
type: "string",
|
|
5245
|
+
required: true,
|
|
5246
|
+
description: "Accessible name for the toolbar landmark. Toolbar mounts as a role='toolbar' region \u2014 without a name, AT users hear an unlabelled landmark. If a visible heading sits adjacent, you may pair via aria-labelledby instead (which still satisfies this requirement)."
|
|
5247
|
+
}
|
|
5248
|
+
],
|
|
5249
|
+
variants: [
|
|
5250
|
+
{
|
|
5251
|
+
name: "orientation",
|
|
5252
|
+
description: "Layout direction.",
|
|
5253
|
+
values: [
|
|
5254
|
+
{
|
|
5255
|
+
value: "horizontal",
|
|
5256
|
+
description: "Default \u2014 controls flow left-to-right.",
|
|
5257
|
+
useWhen: "the toolbar sits at the top or bottom of a panel / editor"
|
|
5258
|
+
},
|
|
5259
|
+
{
|
|
5260
|
+
value: "vertical",
|
|
5261
|
+
description: "Controls flow top-to-bottom.",
|
|
5262
|
+
useWhen: "the toolbar runs alongside a canvas / editor as a side rail"
|
|
5263
|
+
}
|
|
5264
|
+
],
|
|
5265
|
+
default: "horizontal"
|
|
5266
|
+
}
|
|
5267
|
+
],
|
|
5268
|
+
slots: [
|
|
5269
|
+
{
|
|
5270
|
+
name: "children",
|
|
5271
|
+
description: "Mix of ToolbarButton / ToolbarToggleGroup / ToolbarToggleItem / ToolbarLink / ToolbarSeparator.",
|
|
5272
|
+
required: true,
|
|
5273
|
+
acceptedTypes: ["ReactNode"]
|
|
5274
|
+
}
|
|
5275
|
+
],
|
|
5276
|
+
dependencies: {
|
|
5277
|
+
npm: ["@radix-ui/react-toolbar", "class-variance-authority", "clsx", "tailwind-merge"],
|
|
5278
|
+
internal: [],
|
|
5279
|
+
peer: ["react", "react-dom"]
|
|
5280
|
+
},
|
|
5281
|
+
tokensUsed: ["card", "border", "foreground", "accent", "accent-foreground", "ring"],
|
|
5282
|
+
examples: [
|
|
5283
|
+
{
|
|
5284
|
+
title: "Editor toolbar with toggle group",
|
|
5285
|
+
description: "Undo + Redo + Separator + alignment toggles",
|
|
5286
|
+
code: '<Toolbar aria-label="Editor controls">\n <ToolbarButton onClick={onUndo}>Undo</ToolbarButton>\n <ToolbarButton onClick={onRedo}>Redo</ToolbarButton>\n <ToolbarSeparator />\n <ToolbarToggleGroup type="single" defaultValue="left">\n <ToolbarToggleItem value="left">Left</ToolbarToggleItem>\n <ToolbarToggleItem value="center">Center</ToolbarToggleItem>\n <ToolbarToggleItem value="right">Right</ToolbarToggleItem>\n </ToolbarToggleGroup>\n</Toolbar>',
|
|
5287
|
+
composition: ["toolbar", "editor", "toggle-group"]
|
|
5288
|
+
},
|
|
5289
|
+
{
|
|
5290
|
+
title: "Side-rail vertical toolbar",
|
|
5291
|
+
description: "Vertical toolbar alongside a canvas",
|
|
5292
|
+
code: '<Toolbar orientation="vertical" aria-label="Drawing tools">\n <ToolbarToggleGroup type="single" defaultValue="select">\n <ToolbarToggleItem value="select"><CursorIcon /></ToolbarToggleItem>\n <ToolbarToggleItem value="pen"><PenIcon /></ToolbarToggleItem>\n <ToolbarToggleItem value="eraser"><EraserIcon /></ToolbarToggleItem>\n </ToolbarToggleGroup>\n <ToolbarSeparator />\n <ToolbarButton aria-label="Settings"><SettingsIcon /></ToolbarButton>\n</Toolbar>',
|
|
5293
|
+
composition: ["toolbar", "vertical", "canvas", "icon-only"]
|
|
5294
|
+
}
|
|
5295
|
+
],
|
|
5296
|
+
ai: {
|
|
5297
|
+
whenToUse: "Use to group related controls that operate on a single content area (editor, canvas, table). The roving-tabindex behavior means Tab enters the toolbar at the first item, then arrow keys navigate within \u2014 preserves the surrounding page's tab order.",
|
|
5298
|
+
whenNotToUse: "Don't use for app-level menus (use Menubar). Don't use for a single mutually-exclusive selection (use ToggleGroup directly \u2014 Toolbar is for groupING ToggleGroups + Buttons + Links). Don't use for navigation (use NavigationMenu).",
|
|
5299
|
+
commonMistakes: [
|
|
5300
|
+
"Forgetting aria-label when the toolbar isn't adjacent to a visible heading",
|
|
5301
|
+
"Mixing controls that operate on different content areas \u2014 Toolbar implies a single target",
|
|
5302
|
+
"Using Toolbar inside a Toolbar \u2014 nest a vertical Toolbar inside a horizontal one if you really need a 2D grid, but consider whether a single Toolbar with Separators is clearer"
|
|
5303
|
+
],
|
|
5304
|
+
antiPatterns: [
|
|
5305
|
+
{
|
|
5306
|
+
mistake: "Using Toolbar for a single-axis mutually-exclusive selection (alignment, list-style)",
|
|
5307
|
+
insteadUse: "toggle-group",
|
|
5308
|
+
why: "Toolbar is the multi-control container. ToggleGroup directly gives you 'pick one of N' with proper keyboard semantics \u2014 adding a Toolbar wrapper just costs DOM."
|
|
5309
|
+
},
|
|
5310
|
+
{
|
|
5311
|
+
mistake: "Using Toolbar for an application top-level menu (File, Edit, View, \u2026)",
|
|
5312
|
+
insteadUse: "menubar",
|
|
5313
|
+
why: "Menubar models nested menus with proper open-on-focus semantics. Toolbar is for flat command groups; the menu pattern needs Menubar."
|
|
5314
|
+
},
|
|
5315
|
+
{
|
|
5316
|
+
mistake: "Using Toolbar for primary site / app navigation",
|
|
5317
|
+
insteadUse: "navigation-menu",
|
|
5318
|
+
why: "NavigationMenu ships landmark role + skip links + the right ARIA attributes for primary nav. Toolbar is for content-region controls, not page-level routing."
|
|
5319
|
+
}
|
|
5320
|
+
],
|
|
5321
|
+
relatedComponents: ["toggle", "toggle-group", "menubar", "navigation-menu", "separator"],
|
|
5322
|
+
accessibilityNotes: "Radix wires role='toolbar', roving tabindex (Tab enters at the first focusable item, arrows move within), Home/End jump to first/last. Always pass aria-label when there's no visible heading next to the toolbar.",
|
|
5323
|
+
tokenBudget: 600
|
|
5324
|
+
},
|
|
5325
|
+
tags: ["toolbar", "editor", "controls", "command-group"]
|
|
5326
|
+
};
|
|
5327
|
+
|
|
5328
|
+
// src/ai/attachment/attachment.schema.ts
|
|
5329
|
+
var attachmentSchema = {
|
|
5330
|
+
name: "attachment",
|
|
5331
|
+
displayName: "Attachment",
|
|
5332
|
+
description: "File / image thumbnail with optional remove affordance + upload-progress overlay. Composes with the AI Composer for multimodal message drafts. Auto-detects image vs file variant from MIME type + preview URL.",
|
|
5333
|
+
category: "ai",
|
|
5334
|
+
subcategory: "input",
|
|
5335
|
+
props: [
|
|
5336
|
+
{
|
|
5337
|
+
name: "file",
|
|
5338
|
+
type: "object",
|
|
5339
|
+
required: true,
|
|
5340
|
+
description: "Native File OR { name, size, type, preview? } metadata. Drives variant auto-detection (image+preview \u2192 thumbnail; else file icon + name + size)."
|
|
5341
|
+
},
|
|
5342
|
+
{
|
|
5343
|
+
name: "onRemove",
|
|
5344
|
+
type: "function",
|
|
5345
|
+
required: false,
|
|
5346
|
+
description: "Click handler for the \xD7 overlay button. Signature: () => void."
|
|
5347
|
+
},
|
|
5348
|
+
{
|
|
5349
|
+
name: "progress",
|
|
5350
|
+
type: "number",
|
|
5351
|
+
required: false,
|
|
5352
|
+
description: "Upload progress in [0, 1). Values >= 1 (or undefined) hide the progressbar \u2014 pass undefined once the upload completes. Internally scaled to 0\u2013100 for aria-valuenow so AT announces 'NN percent.'"
|
|
5353
|
+
},
|
|
5354
|
+
{
|
|
5355
|
+
name: "variant",
|
|
5356
|
+
type: "enum",
|
|
5357
|
+
required: false,
|
|
5358
|
+
description: "Override the auto-detected variant. 'image' renders a thumbnail; 'file' renders icon + name + size.",
|
|
5359
|
+
enumValues: ["file", "image"]
|
|
5360
|
+
},
|
|
5361
|
+
{ name: "className", type: "string", required: false, description: "Additional CSS classes" }
|
|
5362
|
+
],
|
|
5363
|
+
variants: [
|
|
5364
|
+
{
|
|
5365
|
+
name: "variant",
|
|
5366
|
+
description: "Visual layout. Auto-detected unless overridden.",
|
|
5367
|
+
values: [
|
|
5368
|
+
{
|
|
5369
|
+
value: "file",
|
|
5370
|
+
description: "Generic file icon + name + size. Bordered card with horizontal padding.",
|
|
5371
|
+
useWhen: "the attachment is a non-image (PDF, code, archive, audio) OR an image without a preview URL"
|
|
5372
|
+
},
|
|
5373
|
+
{
|
|
5374
|
+
value: "image",
|
|
5375
|
+
description: "80\xD780 thumbnail of the preview URL. Zero padding so the image fills the card.",
|
|
5376
|
+
useWhen: "the attachment is an image AND a preview URL is available (use URL.createObjectURL on the File before passing in)"
|
|
5377
|
+
}
|
|
5378
|
+
],
|
|
5379
|
+
default: "file"
|
|
5380
|
+
}
|
|
5381
|
+
],
|
|
5382
|
+
slots: [],
|
|
5383
|
+
dependencies: {
|
|
5384
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
5385
|
+
internal: [],
|
|
5386
|
+
peer: ["react", "react-dom"]
|
|
5387
|
+
},
|
|
5388
|
+
tokensUsed: ["card", "border", "foreground", "muted", "muted-foreground", "primary", "accent", "ring"],
|
|
5389
|
+
examples: [
|
|
5390
|
+
{
|
|
5391
|
+
title: "Image preview in composer",
|
|
5392
|
+
description: "Image attachment with remove affordance",
|
|
5393
|
+
code: "function ComposerWithImage() {\n const [file, setFile] = React.useState<File | null>(null);\n const preview = file ? URL.createObjectURL(file) : null;\n return (\n <Composer>\n {file && preview ? (\n <Attachment\n file={{ name: file.name, size: file.size, type: file.type, preview }}\n onRemove={() => setFile(null)}\n />\n ) : null}\n {/* \u2026textarea + send button\u2026 */}\n </Composer>\n );\n}",
|
|
5394
|
+
composition: ["composer", "ai", "image", "removable"]
|
|
5395
|
+
},
|
|
5396
|
+
{
|
|
5397
|
+
title: "Upload progress",
|
|
5398
|
+
description: "File attachment with live progress overlay",
|
|
5399
|
+
code: '<Attachment\n file={{ name: "report.pdf", size: 2_400_000, type: "application/pdf" }}\n progress={uploadProgress}\n onRemove={() => cancelUpload()}\n/>',
|
|
5400
|
+
composition: ["upload", "progress", "removable"]
|
|
5401
|
+
},
|
|
5402
|
+
{
|
|
5403
|
+
title: "Static file preview (no remove)",
|
|
5404
|
+
description: "Read-only attachment in a sent message",
|
|
5405
|
+
code: '<Attachment file={{ name: "diagram.svg", size: 18000, type: "image/svg+xml" }} />',
|
|
5406
|
+
composition: ["read-only", "message"]
|
|
5407
|
+
}
|
|
5408
|
+
],
|
|
5409
|
+
ai: {
|
|
5410
|
+
whenToUse: "Use inside a Composer to show pending attachments the user can remove before sending, or inside a Message to show attached files in a sent turn. Pair native File objects with URL.createObjectURL(file) before rendering \u2014 the component doesn't manage object URLs.",
|
|
5411
|
+
whenNotToUse: "Don't use for the upload-target affordance itself (that's Dropzone \u2014 it handles drag-drop + click-to-pick). Don't use for static document previews outside a chat composer (use Card with a file icon). Don't use for inline images in markdown \u2014 use Markdown's image rendering.",
|
|
5412
|
+
commonMistakes: [
|
|
5413
|
+
"Forgetting URL.createObjectURL on a native File \u2014 preview won't render and the variant falls back to file",
|
|
5414
|
+
"Not revoking object URLs when the attachment is removed \u2014 leaks memory",
|
|
5415
|
+
"Using Attachment without onRemove inside a Composer \u2014 leaves the user unable to remove"
|
|
5416
|
+
],
|
|
5417
|
+
antiPatterns: [
|
|
5418
|
+
{
|
|
5419
|
+
mistake: "Using Attachment as the upload-input affordance (drag-drop area, click-to-pick)",
|
|
5420
|
+
insteadUse: "dropzone",
|
|
5421
|
+
why: "Dropzone is purpose-built for the upload-target \u2014 drag/drop semantics, click-to-pick, accept-MIME validation. Attachment SHOWS the result; Dropzone is the input."
|
|
5422
|
+
},
|
|
5423
|
+
{
|
|
5424
|
+
mistake: "Using Attachment for static document previews on a profile / details page",
|
|
5425
|
+
insteadUse: "card",
|
|
5426
|
+
why: "Card is the static-content surface. Attachment ships remove + progress overlays consumers don't need outside the chat-composer flow."
|
|
5427
|
+
},
|
|
5428
|
+
{
|
|
5429
|
+
mistake: "Using Attachment to render an inline image inside markdown body text",
|
|
5430
|
+
insteadUse: "markdown",
|
|
5431
|
+
why: "Markdown's `` already renders an inline image with the right semantics. Attachment is for the discrete-attachment-token UX (with name, size, remove); markdown handles inline media."
|
|
5432
|
+
}
|
|
5433
|
+
],
|
|
5434
|
+
relatedComponents: ["composer", "dropzone", "message", "message-list"],
|
|
5435
|
+
accessibilityNotes: "Image variant uses <img alt> from the file name. Remove button gets aria-label='Remove ${name}'. Progress overlay is role='progressbar' with aria-valuemin=0 / aria-valuemax=100 / aria-valuenow=Math.round(progress * 100) and a generic 'Uploading ${name}' aria-label \u2014 AT announces 'NN percent.'",
|
|
5436
|
+
tokenBudget: 500
|
|
5437
|
+
},
|
|
5438
|
+
tags: ["attachment", "ai", "composer", "file", "image", "upload", "multimodal"]
|
|
5439
|
+
};
|
|
5440
|
+
|
|
5441
|
+
// src/ai/message/message.schema.ts
|
|
5442
|
+
var messageSchema = {
|
|
5443
|
+
name: "message",
|
|
5444
|
+
displayName: "Message",
|
|
5445
|
+
description: "Single chat-message row scoped to one speaker (user / assistant / system / tool). Headless content slot \u2014 pair with Markdown, CodeBlock, or ToolCall children.",
|
|
5446
|
+
category: "ai",
|
|
5447
|
+
subcategory: "chat",
|
|
5448
|
+
props: [
|
|
5449
|
+
{
|
|
5450
|
+
name: "role",
|
|
5451
|
+
type: "enum",
|
|
5452
|
+
required: true,
|
|
5453
|
+
description: "Speaker. Drives variant styling and the data-role attribute.",
|
|
5454
|
+
enumValues: ["user", "assistant", "system", "tool"]
|
|
5455
|
+
},
|
|
5456
|
+
{
|
|
5457
|
+
name: "children",
|
|
5458
|
+
type: "ReactNode",
|
|
5459
|
+
required: true,
|
|
5460
|
+
description: "Message content. Strings, Markdown, CodeBlock, ToolCall, or any composition."
|
|
5461
|
+
},
|
|
5462
|
+
{
|
|
5463
|
+
name: "className",
|
|
5464
|
+
type: "string",
|
|
5465
|
+
required: false,
|
|
5466
|
+
description: "Additional CSS classes merged onto the row."
|
|
5467
|
+
}
|
|
5468
|
+
],
|
|
5469
|
+
variants: [
|
|
5470
|
+
{
|
|
5471
|
+
name: "role",
|
|
5472
|
+
description: "Visual treatment per speaker.",
|
|
5473
|
+
values: [
|
|
5474
|
+
{ value: "user", description: "Tinted secondary background \u2014 user turns.", useWhen: "The author is the human." },
|
|
5475
|
+
{ value: "assistant", description: "Card background \u2014 model output.", useWhen: "The author is the LLM/agent." },
|
|
5476
|
+
{ value: "system", description: "Muted, italic \u2014 system instructions.", useWhen: "Rendering a visible system prompt or framing message." },
|
|
5477
|
+
{ value: "tool", description: "Accent left-border \u2014 tool messages distinct from assistant text.", useWhen: "Showing raw tool/function output as its own turn." }
|
|
5478
|
+
],
|
|
5479
|
+
default: "assistant"
|
|
5480
|
+
}
|
|
5481
|
+
],
|
|
5482
|
+
slots: [
|
|
5483
|
+
{
|
|
5484
|
+
name: "children",
|
|
5485
|
+
description: "Message body. Compose with Markdown, CodeBlock, ToolCall, or plain text.",
|
|
5486
|
+
required: true,
|
|
5487
|
+
acceptedTypes: ["ReactNode"]
|
|
5488
|
+
}
|
|
5489
|
+
],
|
|
5490
|
+
dependencies: {
|
|
5491
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
5492
|
+
internal: ["lib/utils", "ai/types"],
|
|
5493
|
+
peer: ["react", "react-dom"]
|
|
5494
|
+
},
|
|
5495
|
+
tokensUsed: ["secondary", "foreground", "card", "card-foreground", "muted", "muted-foreground", "accent", "accent-foreground"],
|
|
5496
|
+
examples: [
|
|
5497
|
+
{
|
|
5498
|
+
title: "User and assistant turns",
|
|
5499
|
+
description: "Two messages alternating roles.",
|
|
5500
|
+
code: `<>
|
|
5501
|
+
<Message role="user">What's the capital of France?</Message>
|
|
5502
|
+
<Message role="assistant">Paris.</Message>
|
|
5503
|
+
</>`,
|
|
5504
|
+
composition: ["chat", "turns"]
|
|
5505
|
+
},
|
|
5506
|
+
{
|
|
5507
|
+
title: "Composed assistant turn",
|
|
5508
|
+
description: "Markdown + ToolCall inside one assistant message.",
|
|
5509
|
+
code: '<Message role="assistant">\n <Markdown>{response}</Markdown>\n <ToolCall name="search" state="result" result={hits} />\n</Message>',
|
|
5510
|
+
composition: ["chat", "agent", "tool-use"]
|
|
5511
|
+
}
|
|
5512
|
+
],
|
|
5513
|
+
ai: {
|
|
5514
|
+
whenToUse: "Wrap every conversation turn \u2014 user, assistant, system, or tool. Pair with MessageList for the scrolling viewport. Compose Markdown, CodeBlock, and ToolCall as children for rich assistant turns.",
|
|
5515
|
+
whenNotToUse: "Don't use for non-conversational text (use Card or plain elements). Don't put streaming logic here \u2014 the consumer drives state, Message just renders.",
|
|
5516
|
+
commonMistakes: [
|
|
5517
|
+
"Adding streaming/fetch logic inside Message \u2014 keep it pure",
|
|
5518
|
+
'Using `role="tool"` for assistant text that mentions a tool \u2014 `tool` is for the actual tool turn',
|
|
5519
|
+
"Hard-coding markdown rendering inside Message \u2014 pass <Markdown>{...}</Markdown> as a child instead"
|
|
5520
|
+
],
|
|
5521
|
+
relatedComponents: ["message-list", "markdown", "code-block", "tool-call", "message-actions"],
|
|
5522
|
+
accessibilityNotes: 'Renders as a div with `data-role`. For screen-reader chat semantics, wrap MessageList in `role="log"` and consider `aria-live="polite"` on the streaming container.',
|
|
5523
|
+
tokenBudget: 220
|
|
5524
|
+
},
|
|
5525
|
+
tags: ["ai", "chat", "message", "turn", "conversation"]
|
|
5526
|
+
};
|
|
5527
|
+
|
|
5528
|
+
// src/ai/message-list/message-list.schema.ts
|
|
5529
|
+
var messageListSchema = {
|
|
5530
|
+
name: "message-list",
|
|
5531
|
+
displayName: "MessageList",
|
|
5532
|
+
description: "Auto-scrolling viewport for a chat stream. Pins to the bottom during streaming unless the user scrolls up to read history.",
|
|
5533
|
+
category: "ai",
|
|
5534
|
+
subcategory: "chat",
|
|
5535
|
+
props: [
|
|
5536
|
+
{
|
|
5537
|
+
name: "autoScroll",
|
|
5538
|
+
type: "boolean",
|
|
5539
|
+
required: false,
|
|
5540
|
+
default: true,
|
|
5541
|
+
description: "Pin to bottom on content change (when the user is already near the bottom)."
|
|
5542
|
+
},
|
|
5543
|
+
{
|
|
5544
|
+
name: "children",
|
|
5545
|
+
type: "ReactNode",
|
|
5546
|
+
required: true,
|
|
5547
|
+
description: "Sequence of <Message> elements."
|
|
5548
|
+
},
|
|
5549
|
+
{
|
|
5550
|
+
name: "className",
|
|
5551
|
+
type: "string",
|
|
5552
|
+
required: false,
|
|
5553
|
+
description: "Additional CSS classes (e.g. height, padding)."
|
|
5554
|
+
}
|
|
5555
|
+
],
|
|
5556
|
+
variants: [],
|
|
5557
|
+
slots: [
|
|
5558
|
+
{
|
|
5559
|
+
name: "children",
|
|
5560
|
+
description: "Messages to render in order.",
|
|
5561
|
+
required: true,
|
|
5562
|
+
acceptedTypes: ["ReactNode"]
|
|
5563
|
+
}
|
|
5564
|
+
],
|
|
5565
|
+
dependencies: {
|
|
5566
|
+
npm: ["clsx", "tailwind-merge"],
|
|
5567
|
+
internal: ["lib/utils"],
|
|
5568
|
+
peer: ["react", "react-dom"]
|
|
5569
|
+
},
|
|
5570
|
+
tokensUsed: [],
|
|
5571
|
+
examples: [
|
|
5572
|
+
{
|
|
5573
|
+
title: "Basic chat",
|
|
5574
|
+
description: "Map messages from useChat into a viewport with bottom-pin behavior.",
|
|
5575
|
+
code: '<MessageList className="h-96">\n {messages.map((m) => (\n <Message key={m.id} role={m.role}>{m.content}</Message>\n ))}\n</MessageList>',
|
|
5576
|
+
composition: ["chat", "scroll"]
|
|
5577
|
+
}
|
|
5578
|
+
],
|
|
5579
|
+
ai: {
|
|
5580
|
+
whenToUse: "Wrap a list of <Message> components when you want auto-scroll-on-stream behavior. Always set an explicit height (h-*, max-h-*) on the className so the viewport actually scrolls.",
|
|
5581
|
+
whenNotToUse: "Don't use for static FAQs or non-streaming layouts (use a normal Stack). Don't use as a generic scroll container \u2014 its bottom-pin behavior is opinionated.",
|
|
5582
|
+
commonMistakes: [
|
|
5583
|
+
"Forgetting to set a height \u2014 the viewport won't scroll without `h-*` or `max-h-*`",
|
|
5584
|
+
"Disabling autoScroll while still expecting bottom-pin behavior \u2014 they're the same flag",
|
|
5585
|
+
"Putting non-Message children inside \u2014 a divider or banner breaks the role=log semantics"
|
|
5586
|
+
],
|
|
5587
|
+
relatedComponents: ["message", "composer", "loading-indicator"],
|
|
5588
|
+
accessibilityNotes: 'Renders as `role="log"` with `aria-live="polite"` and `aria-relevant="additions"` so assistive tech announces new turns. If you replace turns wholesale (e.g. on regenerate), pass `aria-relevant="additions removals"` as a prop \u2014 props spread onto the underlying div and override the default.',
|
|
5589
|
+
tokenBudget: 240
|
|
5590
|
+
},
|
|
5591
|
+
tags: ["ai", "chat", "scroll", "viewport", "streaming"]
|
|
5592
|
+
};
|
|
5593
|
+
|
|
5594
|
+
// src/ai/composer/composer.schema.ts
|
|
5595
|
+
var composerSchema = {
|
|
5596
|
+
name: "composer",
|
|
5597
|
+
displayName: "Composer",
|
|
5598
|
+
description: "Multi-line input + submission shell for chat. Submits on Enter (Shift+Enter inserts newline). Trailing slot for attachment + send buttons.",
|
|
5599
|
+
category: "ai",
|
|
5600
|
+
subcategory: "input",
|
|
5601
|
+
props: [
|
|
5602
|
+
{
|
|
5603
|
+
name: "value",
|
|
5604
|
+
type: "string",
|
|
5605
|
+
required: true,
|
|
5606
|
+
description: "Controlled textarea value."
|
|
5607
|
+
},
|
|
5608
|
+
{
|
|
5609
|
+
name: "onValueChange",
|
|
5610
|
+
type: "function",
|
|
5611
|
+
required: true,
|
|
5612
|
+
description: "Called with the new value on each keystroke."
|
|
5613
|
+
},
|
|
5614
|
+
{
|
|
5615
|
+
name: "onSubmit",
|
|
5616
|
+
type: "function",
|
|
5617
|
+
required: true,
|
|
5618
|
+
description: "Called with the trimmed value on Enter or form submit."
|
|
5619
|
+
},
|
|
5620
|
+
{
|
|
5621
|
+
name: "disabled",
|
|
5622
|
+
type: "boolean",
|
|
5623
|
+
required: false,
|
|
5624
|
+
default: false,
|
|
5625
|
+
description: "Lock the input and suppress submission (e.g. during streaming)."
|
|
5626
|
+
},
|
|
5627
|
+
{
|
|
5628
|
+
name: "placeholder",
|
|
5629
|
+
type: "string",
|
|
5630
|
+
required: false,
|
|
5631
|
+
description: "Textarea placeholder copy."
|
|
5632
|
+
},
|
|
5633
|
+
{
|
|
5634
|
+
name: "submitOnEnter",
|
|
5635
|
+
type: "boolean",
|
|
5636
|
+
required: false,
|
|
5637
|
+
default: true,
|
|
5638
|
+
description: "Submit when Enter is pressed without Shift. Disable to require button click."
|
|
5639
|
+
},
|
|
5640
|
+
{
|
|
5641
|
+
name: "children",
|
|
5642
|
+
type: "ReactNode",
|
|
5643
|
+
required: false,
|
|
5644
|
+
description: "Trailing slot \u2014 attachment buttons, voice toggle, send button, etc."
|
|
5645
|
+
},
|
|
5646
|
+
{
|
|
5647
|
+
name: "className",
|
|
5648
|
+
type: "string",
|
|
5649
|
+
required: false,
|
|
5650
|
+
description: "Additional CSS classes on the form wrapper."
|
|
5651
|
+
}
|
|
5652
|
+
],
|
|
5653
|
+
variants: [],
|
|
5654
|
+
slots: [
|
|
5655
|
+
{
|
|
5656
|
+
name: "children",
|
|
5657
|
+
description: "Trailing action buttons rendered after the textarea.",
|
|
5658
|
+
required: false,
|
|
5659
|
+
acceptedTypes: ["ReactNode"]
|
|
5660
|
+
}
|
|
5661
|
+
],
|
|
5662
|
+
dependencies: {
|
|
5663
|
+
npm: ["clsx", "tailwind-merge"],
|
|
5664
|
+
internal: ["lib/utils"],
|
|
5665
|
+
peer: ["react", "react-dom"]
|
|
5666
|
+
},
|
|
5667
|
+
tokensUsed: ["background", "ring", "muted-foreground"],
|
|
5668
|
+
examples: [
|
|
5669
|
+
{
|
|
5670
|
+
title: "Basic composer",
|
|
5671
|
+
description: "Controlled input + send button.",
|
|
5672
|
+
code: 'const [value, setValue] = useState("");\n<Composer\n value={value}\n onValueChange={setValue}\n onSubmit={(v) => { send(v); setValue(""); }}\n placeholder="Ask anything\u2026"\n>\n <Button type="submit" disabled={!value.trim()}>Send</Button>\n</Composer>',
|
|
5673
|
+
composition: ["chat", "form"]
|
|
5674
|
+
},
|
|
5675
|
+
{
|
|
5676
|
+
title: "With attachment slot",
|
|
5677
|
+
description: "Attach button on the leading edge of the trailing slot.",
|
|
5678
|
+
code: '<Composer value={v} onValueChange={setV} onSubmit={send}>\n <Button variant="ghost" size="icon" onClick={pickFile}><Paperclip /></Button>\n <Button type="submit">Send</Button>\n</Composer>',
|
|
5679
|
+
composition: ["chat", "form", "attachment"]
|
|
5680
|
+
}
|
|
5681
|
+
],
|
|
5682
|
+
ai: {
|
|
5683
|
+
whenToUse: "Wrap any user-input surface in an AI app \u2014 chatbots, AI editors, agent prompts. Pair with `useChat` from @ai-sdk/react or any equivalent state hook.",
|
|
5684
|
+
whenNotToUse: "Don't use for non-chat forms (use Form + Textarea). Don't bake fetch/streaming logic into onSubmit \u2014 keep the network call in the consumer.",
|
|
5685
|
+
commonMistakes: [
|
|
5686
|
+
"Calling onSubmit with the raw event instead of the value \u2014 onSubmit receives the trimmed string already",
|
|
5687
|
+
"Forgetting to clear `value` after submit \u2014 Composer is fully controlled",
|
|
5688
|
+
"Wrapping the component in <form> \u2014 Composer renders its own form element"
|
|
5689
|
+
],
|
|
5690
|
+
relatedComponents: ["message-list", "suggestion", "loading-indicator"],
|
|
5691
|
+
accessibilityNotes: "Renders a real <form> + <textarea>, so Enter submission and screen-reader announcements work without extra ARIA. Pass `aria-label` on the wrapper if there's no visible label.",
|
|
5692
|
+
tokenBudget: 280
|
|
5693
|
+
},
|
|
5694
|
+
tags: ["ai", "input", "chat", "form", "composer", "textarea"]
|
|
5695
|
+
};
|
|
5696
|
+
|
|
5697
|
+
// src/ai/loading-indicator/loading-indicator.schema.ts
|
|
5698
|
+
var loadingIndicatorSchema = {
|
|
5699
|
+
name: "loading-indicator",
|
|
5700
|
+
displayName: "LoadingIndicator",
|
|
5701
|
+
description: "Streaming/typing feedback with three motion variants \u2014 dots, pulse, bar.",
|
|
5702
|
+
category: "ai",
|
|
5703
|
+
subcategory: "feedback",
|
|
5704
|
+
props: [
|
|
5705
|
+
{
|
|
5706
|
+
name: "variant",
|
|
5707
|
+
type: "enum",
|
|
5708
|
+
required: false,
|
|
5709
|
+
default: "dots",
|
|
5710
|
+
description: "Animation style.",
|
|
5711
|
+
enumValues: ["dots", "pulse", "bar"]
|
|
5712
|
+
},
|
|
5713
|
+
{
|
|
5714
|
+
name: "size",
|
|
5715
|
+
type: "enum",
|
|
5716
|
+
required: false,
|
|
5717
|
+
default: "md",
|
|
5718
|
+
description: "Text/icon scale.",
|
|
5719
|
+
enumValues: ["sm", "md"]
|
|
5720
|
+
},
|
|
5721
|
+
{
|
|
5722
|
+
name: "label",
|
|
5723
|
+
type: "string",
|
|
5724
|
+
required: false,
|
|
5725
|
+
description: 'Optional adjacent text label (e.g. "Thinking\u2026").'
|
|
5726
|
+
},
|
|
5727
|
+
{
|
|
5728
|
+
name: "className",
|
|
5729
|
+
type: "string",
|
|
5730
|
+
required: false,
|
|
5731
|
+
description: "Additional CSS classes."
|
|
5732
|
+
}
|
|
5733
|
+
],
|
|
5734
|
+
variants: [
|
|
5735
|
+
{
|
|
5736
|
+
name: "variant",
|
|
5737
|
+
description: "Motion style.",
|
|
5738
|
+
values: [
|
|
5739
|
+
{ value: "dots", description: "Three bouncing dots \u2014 classic typing indicator.", useWhen: "You want a familiar chat-style loading affordance." },
|
|
5740
|
+
{ value: "pulse", description: "Single throbbing circle \u2014 minimal.", useWhen: "Tight inline space; one-character signal." },
|
|
5741
|
+
{ value: "bar", description: "Horizontal sweep \u2014 implies progress without claiming a percentage.", useWhen: "Longer-running task (search, retrieval) where dots feel too perpetual." }
|
|
5742
|
+
],
|
|
5743
|
+
default: "dots"
|
|
5744
|
+
}
|
|
5745
|
+
],
|
|
5746
|
+
slots: [],
|
|
5747
|
+
dependencies: {
|
|
5748
|
+
npm: ["class-variance-authority", "clsx", "tailwind-merge"],
|
|
5749
|
+
internal: ["lib/utils"],
|
|
5750
|
+
peer: ["react", "react-dom"]
|
|
5751
|
+
},
|
|
5752
|
+
tokensUsed: ["muted-foreground"],
|
|
5753
|
+
examples: [
|
|
5754
|
+
{
|
|
5755
|
+
title: "Inline typing dots",
|
|
5756
|
+
description: "Render while waiting for the first token.",
|
|
5757
|
+
code: '{isLoading && <LoadingIndicator label="Thinking\u2026" />}',
|
|
5758
|
+
composition: ["chat", "loading"]
|
|
5759
|
+
}
|
|
5760
|
+
],
|
|
5761
|
+
ai: {
|
|
5762
|
+
whenToUse: "Show during the gap between submitting a prompt and the first streamed token, or while a long-running tool call is in flight.",
|
|
5763
|
+
whenNotToUse: "Don't use for known-percent progress (use Progress). Don't keep visible after the first token streams in \u2014 stream itself signals activity.",
|
|
5764
|
+
commonMistakes: [
|
|
5765
|
+
"Leaving the indicator visible while content streams \u2014 overlap looks broken",
|
|
5766
|
+
"Using `bar` for sub-second waits \u2014 the sweep can't complete a cycle"
|
|
5767
|
+
],
|
|
5768
|
+
relatedComponents: ["message", "composer"],
|
|
5769
|
+
accessibilityNotes: 'Renders as `role="status"` with `aria-live="polite"` and an `aria-label`. Pass a meaningful label so screen readers announce "Searching docs" rather than the default "Loading".',
|
|
5770
|
+
tokenBudget: 200
|
|
5771
|
+
},
|
|
5772
|
+
tags: ["ai", "loading", "spinner", "typing", "indicator", "streaming"]
|
|
5773
|
+
};
|
|
5774
|
+
|
|
5775
|
+
// src/ai/suggestion/suggestion.schema.ts
|
|
5776
|
+
var suggestionSchema = {
|
|
5777
|
+
name: "suggestion",
|
|
5778
|
+
displayName: "Suggestion",
|
|
5779
|
+
description: "Clickable prompt pill / quick-action chip. Forwards a payload to onSelect on click.",
|
|
5780
|
+
category: "ai",
|
|
5781
|
+
subcategory: "input",
|
|
5782
|
+
props: [
|
|
5783
|
+
{
|
|
5784
|
+
name: "value",
|
|
5785
|
+
type: "string",
|
|
5786
|
+
required: false,
|
|
5787
|
+
description: "Payload sent to onSelect. Defaults to the rendered children when they are a string."
|
|
5788
|
+
},
|
|
5789
|
+
{
|
|
5790
|
+
name: "onSelect",
|
|
5791
|
+
type: "function",
|
|
5792
|
+
required: true,
|
|
5793
|
+
description: "Called with the payload when the chip is clicked."
|
|
5794
|
+
},
|
|
5795
|
+
{
|
|
5796
|
+
name: "children",
|
|
5797
|
+
type: "ReactNode",
|
|
5798
|
+
required: true,
|
|
5799
|
+
description: "Visible label."
|
|
5800
|
+
},
|
|
5801
|
+
{
|
|
5802
|
+
name: "className",
|
|
5803
|
+
type: "string",
|
|
5804
|
+
required: false,
|
|
5805
|
+
description: "Additional CSS classes."
|
|
5806
|
+
}
|
|
5807
|
+
],
|
|
5808
|
+
variants: [],
|
|
5809
|
+
slots: [
|
|
5810
|
+
{
|
|
5811
|
+
name: "children",
|
|
5812
|
+
description: "Visible label of the chip.",
|
|
5813
|
+
required: true,
|
|
5814
|
+
acceptedTypes: ["ReactNode"]
|
|
5815
|
+
}
|
|
5816
|
+
],
|
|
5817
|
+
dependencies: {
|
|
5818
|
+
npm: ["clsx", "tailwind-merge"],
|
|
5819
|
+
internal: ["lib/utils"],
|
|
5820
|
+
peer: ["react", "react-dom"]
|
|
5821
|
+
},
|
|
5822
|
+
tokensUsed: ["background", "secondary", "ring", "foreground"],
|
|
5823
|
+
examples: [
|
|
5824
|
+
{
|
|
5825
|
+
title: "Starter prompts",
|
|
5826
|
+
description: "Render onboarding suggestions and drop into the composer on click.",
|
|
5827
|
+
code: '<Cluster gap="sm">\n {STARTERS.map((s) => (\n <Suggestion key={s} value={s} onSelect={setInput}>{s}</Suggestion>\n ))}\n</Cluster>',
|
|
5828
|
+
composition: ["chat", "onboarding", "prompts"]
|
|
5829
|
+
}
|
|
5830
|
+
],
|
|
5831
|
+
ai: {
|
|
5832
|
+
whenToUse: "Show 3\u20136 starter prompts before the first turn, or follow-up prompts after an assistant turn. Combine with Cluster for wrap-friendly layout.",
|
|
5833
|
+
whenNotToUse: "Don't use for navigation (use Button or a link). Don't auto-send on click without showing the user what's about to be sent \u2014 set the value into Composer first.",
|
|
5834
|
+
commonMistakes: [
|
|
5835
|
+
"Auto-firing onSelect into useChat.append without populating the input first \u2014 feels surprising",
|
|
5836
|
+
"Long labels \u2014 these are pills, keep under ~40 chars"
|
|
5837
|
+
],
|
|
5838
|
+
relatedComponents: ["composer", "cluster", "button"],
|
|
5839
|
+
accessibilityNotes: "Real <button>, so keyboard activation and screen-reader announcement work natively. If the label is non-text, set `aria-label`.",
|
|
5840
|
+
tokenBudget: 220
|
|
5841
|
+
},
|
|
5842
|
+
tags: ["ai", "suggestion", "prompt", "pill", "chip", "quick-action"]
|
|
5843
|
+
};
|
|
5844
|
+
|
|
5845
|
+
// src/ai/tool-call/tool-call.schema.ts
|
|
5846
|
+
var toolCallSchema = {
|
|
5847
|
+
name: "tool-call",
|
|
5848
|
+
displayName: "ToolCall",
|
|
5849
|
+
description: "Collapsible card showing a tool/function invocation \u2014 name, lifecycle state badge, expandable args + result body.",
|
|
5850
|
+
category: "ai",
|
|
5851
|
+
subcategory: "agent",
|
|
5852
|
+
props: [
|
|
5853
|
+
{
|
|
5854
|
+
name: "name",
|
|
5855
|
+
type: "string",
|
|
5856
|
+
required: true,
|
|
5857
|
+
description: "Tool / function name (e.g. searchDocs, getWeather)."
|
|
5858
|
+
},
|
|
5859
|
+
{
|
|
5860
|
+
name: "state",
|
|
5861
|
+
type: "enum",
|
|
5862
|
+
required: true,
|
|
5863
|
+
description: "Lifecycle of the invocation.",
|
|
5864
|
+
enumValues: ["pending", "running", "result", "error"]
|
|
5865
|
+
},
|
|
5866
|
+
{
|
|
5867
|
+
name: "args",
|
|
5868
|
+
type: "object",
|
|
5869
|
+
required: false,
|
|
5870
|
+
description: "JSON-stringifiable input passed to the tool."
|
|
5871
|
+
},
|
|
5872
|
+
{
|
|
5873
|
+
name: "result",
|
|
5874
|
+
type: "object",
|
|
5875
|
+
required: false,
|
|
5876
|
+
description: "JSON-stringifiable output returned by the tool. May be a string for plain-text results."
|
|
5877
|
+
},
|
|
5878
|
+
{
|
|
5879
|
+
name: "defaultOpen",
|
|
5880
|
+
type: "boolean",
|
|
5881
|
+
required: false,
|
|
5882
|
+
default: false,
|
|
5883
|
+
description: "Start expanded. Default collapsed to keep the chat clean."
|
|
5884
|
+
},
|
|
5885
|
+
{
|
|
5886
|
+
name: "className",
|
|
5887
|
+
type: "string",
|
|
5888
|
+
required: false,
|
|
5889
|
+
description: "Additional CSS classes on the outer Collapsible root."
|
|
5890
|
+
}
|
|
5891
|
+
],
|
|
5892
|
+
variants: [],
|
|
5893
|
+
slots: [],
|
|
5894
|
+
dependencies: {
|
|
5895
|
+
npm: ["@radix-ui/react-collapsible", "clsx", "tailwind-merge"],
|
|
5896
|
+
internal: ["lib/utils", "ai/types"],
|
|
5897
|
+
peer: ["react", "react-dom"]
|
|
5898
|
+
},
|
|
5899
|
+
tokensUsed: ["card", "card-foreground", "muted", "muted-foreground", "primary", "accent", "accent-foreground", "destructive", "ring", "background"],
|
|
5900
|
+
examples: [
|
|
5901
|
+
{
|
|
5902
|
+
title: "Done invocation",
|
|
5903
|
+
description: "Map an AI SDK `tool-*` part to a ToolCall.",
|
|
5904
|
+
code: '<ToolCall\n name="searchDocs"\n state="result"\n args={{ query: "auth" }}\n result={{ hits: docs.length }}\n/>',
|
|
5905
|
+
composition: ["agent", "tool-use"]
|
|
5906
|
+
},
|
|
5907
|
+
{
|
|
5908
|
+
title: "Streaming run",
|
|
5909
|
+
description: "While the tool is executing, show running state without args.",
|
|
5910
|
+
code: '<ToolCall name="getWeather" state="running" args={{ city: "NYC" }} />',
|
|
5911
|
+
composition: ["agent", "tool-use", "streaming"]
|
|
5912
|
+
}
|
|
5913
|
+
],
|
|
5914
|
+
ai: {
|
|
5915
|
+
whenToUse: "Render every tool/function invocation in an agent UI. Map AI SDK v5 `tool-TOOLNAME` parts (state: input-streaming|input-available|output-available|output-error) or LangChain `AIMessage.tool_calls` directly.",
|
|
5916
|
+
whenNotToUse: "Don't use for plain assistant text (use Message). Don't render the tool's actual UI here \u2014 ToolCall is a debug/transparency card; for a rich tool UI, render that as a sibling node inside the Message.",
|
|
5917
|
+
commonMistakes: [
|
|
5918
|
+
"Passing the entire AI SDK part object as `result` \u2014 extract the output payload first",
|
|
5919
|
+
"Setting `defaultOpen` for every call \u2014 quickly clutters the chat",
|
|
5920
|
+
'Using `state="result"` for an error \u2014 use `error` so the user sees red'
|
|
5921
|
+
],
|
|
5922
|
+
relatedComponents: ["message", "reasoning", "loading-indicator"],
|
|
5923
|
+
accessibilityNotes: "Built on Radix Collapsible \u2014 keyboard expand/collapse with Enter/Space works natively. Add `aria-label` on the trigger if the tool name is opaque.",
|
|
5924
|
+
tokenBudget: 320
|
|
5925
|
+
},
|
|
5926
|
+
tags: ["ai", "agent", "tool-call", "function-call", "collapsible", "transparency"]
|
|
5927
|
+
};
|
|
5928
|
+
|
|
5929
|
+
// src/ai/reasoning/reasoning.schema.ts
|
|
5930
|
+
var reasoningSchema = {
|
|
5931
|
+
name: "reasoning",
|
|
5932
|
+
displayName: "Reasoning",
|
|
5933
|
+
description: "Collapsible thinking-trace block for Anthropic-style reasoning or chain-of-thought scratchpads. Optional duration in the header.",
|
|
5934
|
+
category: "ai",
|
|
5935
|
+
subcategory: "agent",
|
|
5936
|
+
props: [
|
|
5937
|
+
{
|
|
5938
|
+
name: "children",
|
|
5939
|
+
type: "ReactNode",
|
|
5940
|
+
required: true,
|
|
5941
|
+
description: "Reasoning content. Wrap with Markdown for formatted thinking traces."
|
|
5942
|
+
},
|
|
5943
|
+
{
|
|
5944
|
+
name: "defaultOpen",
|
|
5945
|
+
type: "boolean",
|
|
5946
|
+
required: false,
|
|
5947
|
+
default: false,
|
|
5948
|
+
description: "Start expanded. Default collapsed."
|
|
5949
|
+
},
|
|
5950
|
+
{
|
|
5951
|
+
name: "durationMs",
|
|
5952
|
+
type: "number",
|
|
5953
|
+
required: false,
|
|
5954
|
+
description: 'Time spent thinking in ms \u2014 renders "Thought for 4.2s" in the header.'
|
|
5955
|
+
},
|
|
5956
|
+
{
|
|
5957
|
+
name: "label",
|
|
5958
|
+
type: "string",
|
|
5959
|
+
required: false,
|
|
5960
|
+
description: "Override the default header label."
|
|
5961
|
+
},
|
|
5962
|
+
{
|
|
5963
|
+
name: "className",
|
|
5964
|
+
type: "string",
|
|
5965
|
+
required: false,
|
|
5966
|
+
description: "Additional CSS classes."
|
|
5967
|
+
}
|
|
5968
|
+
],
|
|
5969
|
+
variants: [],
|
|
5970
|
+
slots: [
|
|
5971
|
+
{
|
|
5972
|
+
name: "children",
|
|
5973
|
+
description: "Reasoning trace body.",
|
|
5974
|
+
required: true,
|
|
5975
|
+
acceptedTypes: ["ReactNode"]
|
|
5976
|
+
}
|
|
5977
|
+
],
|
|
5978
|
+
dependencies: {
|
|
5979
|
+
npm: ["@radix-ui/react-collapsible", "clsx", "tailwind-merge"],
|
|
5980
|
+
internal: ["lib/utils"],
|
|
5981
|
+
peer: ["react", "react-dom"]
|
|
5982
|
+
},
|
|
5983
|
+
tokensUsed: ["foreground", "muted", "muted-foreground", "ring"],
|
|
5984
|
+
examples: [
|
|
5985
|
+
{
|
|
5986
|
+
title: "Thinking trace with duration",
|
|
5987
|
+
description: 'Header reads "Thought for 4.2s" \u2014 body reveals the full trace on click.',
|
|
5988
|
+
code: "<Reasoning durationMs={4200}>\n <Markdown>{thinkingMarkdown}</Markdown>\n</Reasoning>",
|
|
5989
|
+
composition: ["agent", "reasoning", "anthropic"]
|
|
5990
|
+
}
|
|
5991
|
+
],
|
|
5992
|
+
ai: {
|
|
5993
|
+
whenToUse: "Render Anthropic `thinking` blocks, chain-of-thought scratchpads, or any model-internal reasoning the user can optionally inspect.",
|
|
5994
|
+
whenNotToUse: "Don't use for the assistant's actual answer (use Message). Don't auto-open for routine turns \u2014 only worth expanding for debug or trust-building UX.",
|
|
5995
|
+
commonMistakes: [
|
|
5996
|
+
"Showing reasoning that wasn't actually produced \u2014 fabricating thinking blocks erodes trust",
|
|
5997
|
+
"`durationMs` measured from request start instead of model thinking time \u2014 use the model's reported duration when available"
|
|
5998
|
+
],
|
|
5999
|
+
relatedComponents: ["message", "tool-call", "markdown"],
|
|
6000
|
+
accessibilityNotes: "Radix Collapsible \u2014 keyboard expand/collapse via Enter/Space. The italic header is decorative; use the visible label for screen readers.",
|
|
6001
|
+
tokenBudget: 280
|
|
6002
|
+
},
|
|
6003
|
+
tags: ["ai", "agent", "reasoning", "thinking", "chain-of-thought", "collapsible"]
|
|
6004
|
+
};
|
|
6005
|
+
|
|
6006
|
+
// src/ai/message-actions/message-actions.schema.ts
|
|
6007
|
+
var messageActionsSchema = {
|
|
6008
|
+
name: "message-actions",
|
|
6009
|
+
displayName: "MessageActions",
|
|
6010
|
+
description: "Inline action-button row beneath a message \u2014 copy, regenerate, thumbs-up/down. Pure container; consumer supplies the buttons + handlers.",
|
|
6011
|
+
category: "ai",
|
|
6012
|
+
subcategory: "chat",
|
|
6013
|
+
props: [
|
|
6014
|
+
{
|
|
6015
|
+
name: "children",
|
|
6016
|
+
type: "ReactNode",
|
|
6017
|
+
required: true,
|
|
6018
|
+
description: 'Action buttons (typically Button variant="ghost" size="icon").'
|
|
6019
|
+
},
|
|
6020
|
+
{
|
|
6021
|
+
name: "className",
|
|
6022
|
+
type: "string",
|
|
6023
|
+
required: false,
|
|
6024
|
+
description: "Additional CSS classes."
|
|
6025
|
+
}
|
|
6026
|
+
],
|
|
6027
|
+
variants: [],
|
|
6028
|
+
slots: [
|
|
6029
|
+
{
|
|
6030
|
+
name: "children",
|
|
6031
|
+
description: "Action buttons rendered in a horizontal row.",
|
|
6032
|
+
required: true,
|
|
6033
|
+
acceptedTypes: ["ReactNode"]
|
|
6034
|
+
}
|
|
6035
|
+
],
|
|
6036
|
+
dependencies: {
|
|
6037
|
+
npm: ["clsx", "tailwind-merge"],
|
|
6038
|
+
internal: ["lib/utils"],
|
|
6039
|
+
peer: ["react", "react-dom"]
|
|
6040
|
+
},
|
|
6041
|
+
tokensUsed: [],
|
|
6042
|
+
examples: [
|
|
6043
|
+
{
|
|
6044
|
+
title: "Copy + regenerate",
|
|
6045
|
+
description: "Two ghost icon buttons under an assistant message.",
|
|
6046
|
+
code: '<MessageActions>\n <Button variant="ghost" size="icon" onClick={() => navigator.clipboard.writeText(text)}><CopyIcon /></Button>\n <Button variant="ghost" size="icon" onClick={onRegenerate}><RetryIcon /></Button>\n</MessageActions>',
|
|
6047
|
+
composition: ["chat", "actions"]
|
|
6048
|
+
}
|
|
6049
|
+
],
|
|
6050
|
+
ai: {
|
|
6051
|
+
whenToUse: "Render after assistant turns where the user might want to copy, regenerate, or rate the response. Combine with `group/message` on the parent Message to enable hover-reveal.",
|
|
6052
|
+
whenNotToUse: "Don't use under user turns (no actions to take). Don't put primary CTAs here \u2014 actions row is for secondary, optional follow-ups.",
|
|
6053
|
+
commonMistakes: [
|
|
6054
|
+
"Forgetting `group/message` on the parent Message \u2014 hover-reveal won't trigger",
|
|
6055
|
+
"Stacking too many buttons (more than ~4) \u2014 overwhelms the chat"
|
|
6056
|
+
],
|
|
6057
|
+
relatedComponents: ["message", "button"],
|
|
6058
|
+
accessibilityNotes: "Pure layout container. Each child button must carry its own `aria-label` since icon-only buttons have no accessible text.",
|
|
6059
|
+
tokenBudget: 180
|
|
6060
|
+
},
|
|
6061
|
+
tags: ["ai", "chat", "actions", "row", "container"]
|
|
6062
|
+
};
|
|
6063
|
+
|
|
6064
|
+
// src/ai/citation/citation.schema.ts
|
|
6065
|
+
var citationSchema = {
|
|
6066
|
+
name: "citation",
|
|
6067
|
+
displayName: "Citation",
|
|
6068
|
+
description: "Source attribution chip \u2014 renders a citation for a RAG hit, search result, or any external reference. Becomes a focusable link when url is provided.",
|
|
6069
|
+
category: "ai",
|
|
6070
|
+
subcategory: "rag",
|
|
6071
|
+
props: [
|
|
6072
|
+
{
|
|
6073
|
+
name: "title",
|
|
6074
|
+
type: "string",
|
|
6075
|
+
required: true,
|
|
6076
|
+
description: 'Source title or filename (e.g. "auth-overview.md").'
|
|
6077
|
+
},
|
|
6078
|
+
{
|
|
6079
|
+
name: "url",
|
|
6080
|
+
type: "string",
|
|
6081
|
+
required: false,
|
|
6082
|
+
description: "If provided, the chip becomes an anchor opening in a new tab."
|
|
6083
|
+
},
|
|
6084
|
+
{
|
|
6085
|
+
name: "page",
|
|
6086
|
+
type: "number",
|
|
6087
|
+
required: false,
|
|
6088
|
+
description: 'Optional page number (renders "p.3").'
|
|
6089
|
+
},
|
|
6090
|
+
{
|
|
6091
|
+
name: "index",
|
|
6092
|
+
type: "number",
|
|
6093
|
+
required: false,
|
|
6094
|
+
description: 'Optional inline footnote number (renders "[1]").'
|
|
6095
|
+
},
|
|
6096
|
+
{
|
|
6097
|
+
name: "children",
|
|
6098
|
+
type: "ReactNode",
|
|
6099
|
+
required: false,
|
|
6100
|
+
description: "Trailing slot \u2014 extra metadata (e.g. relevance score)."
|
|
6101
|
+
},
|
|
6102
|
+
{
|
|
6103
|
+
name: "className",
|
|
6104
|
+
type: "string",
|
|
6105
|
+
required: false,
|
|
6106
|
+
description: "Additional CSS classes."
|
|
6107
|
+
}
|
|
6108
|
+
],
|
|
6109
|
+
variants: [],
|
|
6110
|
+
slots: [
|
|
6111
|
+
{
|
|
6112
|
+
name: "children",
|
|
6113
|
+
description: "Trailing extras \u2014 relevance score, badge, etc.",
|
|
6114
|
+
required: false,
|
|
6115
|
+
acceptedTypes: ["ReactNode"]
|
|
6116
|
+
}
|
|
6117
|
+
],
|
|
6118
|
+
dependencies: {
|
|
6119
|
+
npm: ["clsx", "tailwind-merge"],
|
|
6120
|
+
internal: ["lib/utils"],
|
|
6121
|
+
peer: ["react", "react-dom"]
|
|
6122
|
+
},
|
|
6123
|
+
tokensUsed: ["card", "secondary", "ring", "foreground", "muted-foreground"],
|
|
6124
|
+
examples: [
|
|
6125
|
+
{
|
|
6126
|
+
title: "Footnote-style citations",
|
|
6127
|
+
description: "Numbered citations after an assistant turn.",
|
|
6128
|
+
code: '<Cluster gap="xs">\n {sources.map((s, i) => (\n <Citation key={s.id} title={s.title} url={s.url} index={i + 1} />\n ))}\n</Cluster>',
|
|
6129
|
+
composition: ["rag", "citations"]
|
|
6130
|
+
}
|
|
6131
|
+
],
|
|
6132
|
+
ai: {
|
|
6133
|
+
whenToUse: "Surface RAG/search-result sources after an assistant message. Use `index` for footnote-style numbering tied to inline `[1]` markers in the answer.",
|
|
6134
|
+
whenNotToUse: "Don't use for general external links in chat (use Markdown). Don't fabricate sources \u2014 only render Citation for retrievals the model actually used.",
|
|
6135
|
+
commonMistakes: [
|
|
6136
|
+
"Showing the URL twice (in `title` AND `url`) \u2014 pick a human title separately",
|
|
6137
|
+
'Skipping `target="_blank"` \u2014 handled automatically when url is set; don\'t override'
|
|
6138
|
+
],
|
|
6139
|
+
relatedComponents: ["message", "cluster"],
|
|
6140
|
+
accessibilityNotes: 'With `url`, renders a real anchor (`rel="noreferrer noopener"`). Without `url`, renders a static span. Add `aria-label` on the wrapper if title alone lacks context.',
|
|
6141
|
+
tokenBudget: 240
|
|
6142
|
+
},
|
|
6143
|
+
tags: ["ai", "rag", "citation", "source", "footnote", "chip"]
|
|
6144
|
+
};
|
|
6145
|
+
|
|
6146
|
+
// src/ai/markdown/markdown.schema.ts
|
|
6147
|
+
var markdownSchema = {
|
|
6148
|
+
name: "markdown",
|
|
6149
|
+
displayName: "Markdown",
|
|
6150
|
+
description: "Streaming-safe markdown renderer wrapping Vercel's streamdown. Handles partial input mid-stream without throwing.",
|
|
6151
|
+
category: "ai",
|
|
6152
|
+
subcategory: "content",
|
|
6153
|
+
props: [
|
|
6154
|
+
{
|
|
6155
|
+
name: "children",
|
|
6156
|
+
type: "string",
|
|
6157
|
+
required: true,
|
|
6158
|
+
description: "Raw markdown. May be a partial chunk during streaming."
|
|
6159
|
+
},
|
|
6160
|
+
{
|
|
6161
|
+
name: "className",
|
|
6162
|
+
type: "string",
|
|
6163
|
+
required: false,
|
|
6164
|
+
description: "Additional CSS classes on the root element."
|
|
6165
|
+
}
|
|
6166
|
+
],
|
|
6167
|
+
variants: [],
|
|
6168
|
+
slots: [
|
|
6169
|
+
{
|
|
6170
|
+
name: "children",
|
|
6171
|
+
description: "Markdown source as a string.",
|
|
6172
|
+
required: true,
|
|
6173
|
+
acceptedTypes: ["string"]
|
|
6174
|
+
}
|
|
6175
|
+
],
|
|
6176
|
+
dependencies: {
|
|
6177
|
+
npm: ["streamdown", "clsx", "tailwind-merge"],
|
|
6178
|
+
internal: ["lib/utils"],
|
|
6179
|
+
peer: ["react", "react-dom"]
|
|
6180
|
+
},
|
|
6181
|
+
tokensUsed: ["foreground", "primary"],
|
|
6182
|
+
examples: [
|
|
6183
|
+
{
|
|
6184
|
+
title: "Streaming assistant turn",
|
|
6185
|
+
description: "Render the in-flight assistant message as it arrives.",
|
|
6186
|
+
code: '<Message role="assistant">\n <Markdown>{message.content}</Markdown>\n</Message>',
|
|
6187
|
+
composition: ["chat", "streaming", "markdown"]
|
|
6188
|
+
},
|
|
6189
|
+
{
|
|
6190
|
+
title: "Custom rendering: drop down to Streamdown",
|
|
6191
|
+
description: "For per-element overrides, use streamdown directly with our CodeBlock primitive.",
|
|
6192
|
+
code: 'import { Streamdown } from "streamdown";\nimport { CodeBlock } from "@hex-core/components";\n\n<Streamdown components={{ pre: ({ children }) => <CodeBlock code={extractCode(children)} /> }}>\n {markdown}\n</Streamdown>',
|
|
6193
|
+
composition: ["chat", "code", "override"]
|
|
6194
|
+
}
|
|
6195
|
+
],
|
|
6196
|
+
ai: {
|
|
6197
|
+
whenToUse: "Render any markdown content from an LLM \u2014 assistant turns, system messages with formatting, RAG citations with inline links. Required for streaming because raw markdown parsers throw on unfinished input.",
|
|
6198
|
+
whenNotToUse: "Don't use for plain text without formatting (just render the string). Don't bypass it for streamed content \u2014 partial input WILL break a non-streaming parser.",
|
|
6199
|
+
commonMistakes: [
|
|
6200
|
+
"Passing JSX children instead of a markdown string \u2014 Markdown only accepts strings",
|
|
6201
|
+
"Trying to override per-element renderers via Markdown \u2014 drop down to `Streamdown` directly for that (we keep our public surface minimal so the DTS bundle doesn't drag in shiki's giant language union)",
|
|
6202
|
+
"Forgetting Tailwind Typography (`prose`) classes are required to style the output"
|
|
6203
|
+
],
|
|
6204
|
+
relatedComponents: ["message", "code-block", "citation"],
|
|
6205
|
+
accessibilityNotes: "Inherits semantics from streamdown: real headings, lists, links. Verify Tailwind Typography (prose) is enabled in your CSS \u2014 without it, output renders unstyled.",
|
|
6206
|
+
tokenBudget: 280
|
|
6207
|
+
},
|
|
6208
|
+
tags: ["ai", "markdown", "streaming", "content", "renderer"]
|
|
6209
|
+
};
|
|
6210
|
+
|
|
6211
|
+
// src/ai/code-block/code-block.schema.ts
|
|
6212
|
+
var codeBlockSchema = {
|
|
6213
|
+
name: "code-block",
|
|
6214
|
+
displayName: "CodeBlock",
|
|
6215
|
+
description: "Server-rendered, syntax-highlighted code block with a language-label header and a copy button. Dual-theme via Shiki \u2014 same HTML for light + dark.",
|
|
6216
|
+
category: "ai",
|
|
6217
|
+
subcategory: "content",
|
|
6218
|
+
props: [
|
|
6219
|
+
{
|
|
6220
|
+
name: "code",
|
|
6221
|
+
type: "string",
|
|
6222
|
+
required: true,
|
|
6223
|
+
description: "The code to display. Plain text \u2014 no markdown fences."
|
|
6224
|
+
},
|
|
6225
|
+
{
|
|
6226
|
+
name: "label",
|
|
6227
|
+
type: "string",
|
|
6228
|
+
required: false,
|
|
6229
|
+
description: 'Header label (e.g. "pnpm", "tsx"). Inferred from `language` if omitted.'
|
|
6230
|
+
},
|
|
6231
|
+
{
|
|
6232
|
+
name: "language",
|
|
6233
|
+
type: "enum",
|
|
6234
|
+
required: false,
|
|
6235
|
+
description: "Shiki grammar key. Overrides inference from `label`.",
|
|
6236
|
+
enumValues: ["bash", "ts", "tsx", "js", "jsx", "json", "css", "html", "md", "py", "text"]
|
|
6237
|
+
},
|
|
6238
|
+
{
|
|
6239
|
+
name: "themes",
|
|
6240
|
+
type: "object",
|
|
6241
|
+
required: false,
|
|
6242
|
+
default: { light: "github-light-high-contrast", dark: "github-dark" },
|
|
6243
|
+
description: "Override the default theme pair. Keys: `light`, `dark` \u2014 values are Shiki theme IDs."
|
|
6244
|
+
},
|
|
6245
|
+
{
|
|
6246
|
+
name: "className",
|
|
6247
|
+
type: "string",
|
|
6248
|
+
required: false,
|
|
6249
|
+
description: "Additional CSS classes on the outer card."
|
|
6250
|
+
}
|
|
6251
|
+
],
|
|
6252
|
+
variants: [],
|
|
6253
|
+
slots: [],
|
|
6254
|
+
dependencies: {
|
|
6255
|
+
npm: ["shiki", "clsx", "tailwind-merge"],
|
|
6256
|
+
internal: ["lib/utils", "ai/code-block-copy"],
|
|
6257
|
+
peer: ["react", "react-dom"]
|
|
6258
|
+
},
|
|
6259
|
+
tokensUsed: ["card", "card-foreground", "muted", "muted-foreground", "border"],
|
|
6260
|
+
examples: [
|
|
6261
|
+
{
|
|
6262
|
+
title: "Install command",
|
|
6263
|
+
description: "Header label drives both the visible chip and Shiki grammar inference.",
|
|
6264
|
+
code: '<CodeBlock label="pnpm" code="pnpm add @hex-core/components" />',
|
|
6265
|
+
composition: ["docs", "install"]
|
|
6266
|
+
},
|
|
6267
|
+
{
|
|
6268
|
+
title: "Inline TS sample",
|
|
6269
|
+
description: "Explicit language overrides label inference.",
|
|
6270
|
+
code: '<CodeBlock language="tsx" code={`<Button>Click</Button>`} />',
|
|
6271
|
+
composition: ["docs", "code"]
|
|
6272
|
+
}
|
|
6273
|
+
],
|
|
6274
|
+
ai: {
|
|
6275
|
+
whenToUse: "Render any code snippet in docs, copy-to-clipboard install commands, or static AI chat output where server rendering is acceptable. Pair with Markdown's `components.pre` override to take over markdown code fences.",
|
|
6276
|
+
whenNotToUse: "Don't use for streaming chat where the code grows mid-render \u2014 async Server Components can't update token-by-token. Use Streamdown's built-in client CodeBlock for that.",
|
|
6277
|
+
commonMistakes: [
|
|
6278
|
+
"Passing markdown-fenced code (with ```) \u2014 strip the fences first",
|
|
6279
|
+
"Forgetting that this is async \u2014 must be awaited or rendered as RSC",
|
|
6280
|
+
"Using a Shiki theme that isn't bundled \u2014 fails with a runtime fetch error"
|
|
6281
|
+
],
|
|
6282
|
+
relatedComponents: ["markdown", "message"],
|
|
6283
|
+
accessibilityNotes: "Highlighted output is plain text inside a `<pre>` \u2014 screen readers read it normally. The copy button has its own `aria-label`. Add a meaningful `aria-label` on the wrapper if the label alone isn't descriptive.",
|
|
6284
|
+
tokenBudget: 320
|
|
6285
|
+
},
|
|
6286
|
+
tags: ["ai", "code", "shiki", "highlight", "copy", "rsc"]
|
|
6287
|
+
};
|
|
6288
|
+
|
|
6289
|
+
export { accordionSchema, alertDialogSchema, alertSchema, aspectRatioSchema, attachmentSchema, avatarSchema, badgeSchema, breadcrumbSchema, buttonSchema, calendarSchema, cardSchema, checkboxSchema, citationSchema, clusterSchema, codeBlockSchema, collapsibleSchema, colorPickerSchema, comboboxSchema, commandSchema, composerSchema, containerSchema, contextMenuSchema, dataTableSchema, datePickerSchema, dialogSchema, drawerSchema, dropdownMenuSchema, dropzoneSchema, emptySchema, errorStateSchema, fileTreeSchema, formSchema, gridSchema, hoverCardSchema, inputOTPSchema, inputSchema, labelSchema, loadingIndicatorSchema, loadingSchema, markdownSchema, menubarSchema, messageActionsSchema, messageListSchema, messageSchema, multiComboboxSchema, navigationMenuSchema, paginationSchema, popoverSchema, progressSchema, radioGroupSchema, reasoningSchema, resizableSchema, scrollAreaSchema, selectSchema, separatorSchema, sheetSchema, sidebarSchema, skeletonSchema, sliderSchema, sonnerSchema, spacerSchema, stackSchema, stepperSchema, suggestionSchema, switchSchema, tableSchema, tabsSchema, tagSchema, textareaSchema, timePickerSchema, timelineSchema, toggleGroupSchema, toggleSchema, toolCallSchema, toolbarSchema, tooltipSchema, treeSchema };
|
|
4642
6290
|
//# sourceMappingURL=schemas.js.map
|
|
4643
6291
|
//# sourceMappingURL=schemas.js.map
|