@usevyre/ai-context 1.0.1 → 1.2.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.
@@ -1,5 +1,5 @@
1
1
  # useVyre Rules for Windsurf
2
- # Version: 1.0.0
2
+ # Version: 1.2.0
3
3
 
4
4
  # useVyre Design System — AI Context
5
5
  # Version: 0.2.0
@@ -308,7 +308,7 @@ import { Button } from "@usevyre/react"
308
308
 
309
309
  ### Calendar
310
310
 
311
- Date picker calendar widget for selecting single dates or ranges.
311
+ Inline date-grid widget (always visible, no input). mode: single | range | multiple, optional time picker. For an input + popover use DatePicker; for start/end ranges with presets use DateRangePicker.
312
312
 
313
313
  ```tsx
314
314
  import { Calendar } from "@usevyre/react"
@@ -317,6 +317,7 @@ import { Calendar } from "@usevyre/react"
317
317
  // value = Date | null
318
318
  // onChange = function
319
319
  // disabled = boolean (default: false)
320
+ // defaultMonth = Date
320
321
 
321
322
  // Examples:
322
323
  const [date, setDate] = useState(null);
@@ -324,7 +325,39 @@ const [date, setDate] = useState(null);
324
325
  ```
325
326
 
326
327
  **Common mistakes:**
327
- - ❌ `Using Calendar for time selection` Combine with a separate time Input if time selection is needed
328
+ - ❌ `Calendar for an input field that opens a popover` Use <DatePicker /> (single date) or <DateRangePicker /> (range)
329
+ - ❌ `value as tuple for mode="single"` → Pass value matching mode; use mode="range" for [start,end]
330
+
331
+ ---
332
+
333
+ ### DatePicker
334
+
335
+ Input trigger that opens a Calendar in a popover. Same modes as Calendar (single | range | multiple) plus a placeholder. Use this for a compact date field; use Calendar for an always-visible grid, or DateRangePicker for start/end ranges with presets.
336
+
337
+ ```tsx
338
+ import { DatePicker } from "@usevyre/react"
339
+
340
+ // Props:
341
+ // value = Date | [Date, Date] | Date[] | null
342
+ // onChange = function
343
+ // mode = "single" | "range" | "multiple" (default: single)
344
+ // placeholder = string (default: Pick a date)
345
+ // showTime = boolean (default: false)
346
+ // minDate = Date
347
+ // maxDate = Date
348
+ // disabled = function
349
+ // weekStartsOn = "0" | "1" (default: 1)
350
+ // inputClassName = string
351
+
352
+ // Examples:
353
+ const [date, setDate] = useState(null);
354
+ <DatePicker value={date} onChange={setDate} placeholder="Pick a date" />
355
+ <DatePicker value={date} onChange={setDate} showTime />
356
+ ```
357
+
358
+ **Common mistakes:**
359
+ - ❌ `DatePicker mode="range" for { from, to } object` → Use <DateRangePicker /> for the { from, to } object API + presets + dual month
360
+ - ❌ `DatePicker without value/onChange` → Provide value and onChange (e.g. from useState)
328
361
 
329
362
  ---
330
363
 
@@ -384,6 +417,78 @@ import { Checkbox } from "@usevyre/react"
384
417
 
385
418
  ---
386
419
 
420
+ ### RadioGroup
421
+
422
+ Controlled single-choice group. RadioGroup owns the selected value; render it data-driven via the options array OR with composable <Radio> children for custom content. role=radiogroup with proper labelling. For multi-select use Checkbox; for a compact dropdown use Select.
423
+
424
+ ```tsx
425
+ import { RadioGroup, Radio } from "@usevyre/react"
426
+
427
+ // Props:
428
+ // value = string
429
+ // defaultValue = string
430
+ // onChange = function
431
+ // name = string
432
+ // disabled = boolean (default: false)
433
+ // size = "sm" | "md" (default: md)
434
+ // orientation = "vertical" | "horizontal" (default: vertical)
435
+ // options = { value: string; label?: string; description?: string; disabled?: boolean }[]
436
+
437
+ // Examples:
438
+ <RadioGroup
439
+ value={plan}
440
+ onChange={setPlan}
441
+ options={[
442
+ { value: "free", label: "Free", description: "For hobby projects" },
443
+ { value: "pro", label: "Pro", description: "For teams" },
444
+ ]}
445
+ />
446
+ <RadioGroup value={plan} onChange={setPlan} orientation="horizontal">
447
+ <Radio value="free" label="Free" />
448
+ <Radio value="pro" label="Pro" />
449
+ </RadioGroup>
450
+ ```
451
+
452
+ **Common mistakes:**
453
+ - ❌ `<Radio> used outside a <RadioGroup>` → Always wrap <Radio> in <RadioGroup>
454
+ - ❌ `RadioGroup without value/onChange (React) or v-model (Vue)` → Bind value + onChange (React) or v-model (Vue); or defaultValue for uncontrolled in React
455
+ - ❌ `Using Checkbox for mutually-exclusive choices` → Use RadioGroup + Radio (or options) for one-of-many
456
+
457
+ ---
458
+
459
+ ### RichTextEditor
460
+
461
+ Controlled WYSIWYG editor. value is an HTML string; you own it in state and set it in onChange (React) / v-model (Vue). Native contentEditable + execCommand — zero dependencies. Toolbar: bold, italic, underline, strike, h1-h3, ordered/unordered lists, quote, code block, link, clear formatting.
462
+
463
+ ```tsx
464
+ import { RichTextEditor } from "@usevyre/react"
465
+
466
+ // Props:
467
+ // value = string
468
+ // onChange = function
469
+ // placeholder = string (default: Write something…)
470
+ // disabled = boolean (default: false)
471
+ // readOnly = boolean (default: false)
472
+ // toolbar = RichTextTool[]
473
+ // minHeight = string (default: 10rem)
474
+
475
+ // Examples:
476
+ const [html, setHtml] = useState("<p>Hello <strong>world</strong></p>");
477
+ <RichTextEditor value={html} onChange={setHtml} placeholder="Write…" />
478
+ <RichTextEditor
479
+ value={html}
480
+ onChange={setHtml}
481
+ toolbar={["bold", "italic", "link"]}
482
+ />
483
+ ```
484
+
485
+ **Common mistakes:**
486
+ - ❌ `RichTextEditor without value/onChange (React) or v-model (Vue)` → Keep the HTML string in state and update it in onChange / v-model
487
+ - ❌ `Rendering value as text or with dangerouslySetInnerHTML elsewhere without sanitising` → Sanitise (e.g. DOMPurify) before re-rendering untrusted RTE output
488
+ - ❌ `toolbar="bold" (string)` → Pass an array, e.g. toolbar={["bold","italic","link"]}
489
+
490
+ ---
491
+
387
492
  ### Command
388
493
 
389
494
  Command palette / search dialog. Use for search-first navigation or quick actions.
@@ -435,7 +540,7 @@ import { DropdownMenu, DropdownItem, DropdownSeparator, DropdownCheckboxItem, Dr
435
540
 
436
541
  ### Field
437
542
 
438
- Form field wrapper providing label, hint text, and validation state for Input or Textarea.
543
+ Form field wrapper. Two ways to use it (both supported): (1) props-based — pass label/hint/state/required for the common case; (2) composable — use the parts FieldLabel, FieldDescription, FieldError, FieldGroup, FieldSet for richer layouts (multiple controls, custom error placement). The props-based API is unchanged and still works.
439
544
 
440
545
  ```tsx
441
546
  import { Field, Input, Textarea } from "@usevyre/react"
@@ -453,10 +558,23 @@ import { Field, Input, Textarea } from "@usevyre/react"
453
558
  <Field label="Search">
454
559
  <Input leftElement={<SearchIcon />} placeholder="Search..." />
455
560
  </Field>
561
+ <Field>
562
+ <FieldLabel required htmlFor="email">Email</FieldLabel>
563
+ <Input id="email" type="email" />
564
+ <FieldDescription>We\u2019ll never share it.</FieldDescription>
565
+ <FieldError>{errors.email}</FieldError>
566
+ </Field>
567
+
568
+ // Two controls side by side
569
+ <FieldGroup orientation="horizontal">
570
+ <Field label="First name"><Input /></Field>
571
+ <Field label="Last name"><Input /></Field>
572
+ </FieldGroup>
456
573
  ```
457
574
 
458
575
  **Common mistakes:**
459
576
  - ❌ `Applying state prop directly to Input` → Wrap Input in <Field state="error"> to apply validation styling
577
+ - ❌ `Mixing props label/hint AND FieldLabel/FieldError for the same field` → Pick one: either props-based (label/hint/state) OR composable parts
460
578
 
461
579
  ---
462
580
 
@@ -468,6 +586,7 @@ Text input field. Wrap in Field for labels and validation. Use leftElement/right
468
586
  import { Input } from "@usevyre/react"
469
587
 
470
588
  // Props:
589
+ // modelValue = string | number
471
590
  // size = "sm" | "md" | "lg" (default: md)
472
591
  // leftElement = ReactNode
473
592
  // rightElement = ReactNode
@@ -479,6 +598,7 @@ import { Input } from "@usevyre/react"
479
598
  **Common mistakes:**
480
599
  - ❌ `size="icon"` → Use size="sm" | "md" | "lg"
481
600
  - ❌ `type="search" for search UI` → Import Command from @usevyre/react for search palettes
601
+ - ❌ `Vue: binding Input/Textarea value without v-model` → Use v-model on <Input>/<Textarea> in Vue; in React use value + onChange
482
602
 
483
603
  ---
484
604
 
@@ -884,6 +1004,362 @@ import { Text, Heading, Lead, Code, Blockquote } from "@usevyre/react"
884
1004
 
885
1005
  ---
886
1006
 
1007
+ ### ButtonGroup
1008
+
1009
+ Groups multiple Button components into one visual unit (toolbar, segmented control). Pure layout — no internal state.
1010
+
1011
+ ```tsx
1012
+ import { ButtonGroup, Button } from "@usevyre/react"
1013
+
1014
+ // Props:
1015
+ // orientation = "horizontal" | "vertical" (default: horizontal)
1016
+ // attached = boolean (default: false)
1017
+ // size = "sm" | "md" | "lg" | "icon"
1018
+
1019
+ // Examples:
1020
+ <ButtonGroup attached>
1021
+ <Button variant="secondary">Day</Button>
1022
+ <Button variant="secondary">Week</Button>
1023
+ <Button variant="secondary">Month</Button>
1024
+ </ButtonGroup>
1025
+ <ButtonGroup orientation="vertical" attached>
1026
+ <Button variant="secondary">Top</Button>
1027
+ <Button variant="secondary">Bottom</Button>
1028
+ </ButtonGroup>
1029
+ ```
1030
+
1031
+ **Common mistakes:**
1032
+ - ❌ `ButtonGroup variant="..."` → Set variant on each <Button> inside the group
1033
+ - ❌ `ButtonGroup without Button children` → Place <Button> elements as direct children
1034
+
1035
+ ---
1036
+
1037
+ ### TagsInput
1038
+
1039
+ Multi-tag input. Type and press Enter or comma to add a tag, click x to remove, Backspace on empty input removes the last tag. Controlled.
1040
+
1041
+ ```tsx
1042
+ import { TagsInput } from "@usevyre/react"
1043
+
1044
+ // Props:
1045
+ // value = string[]
1046
+ // onChange = (tags: string[]) => void
1047
+ // placeholder = string
1048
+ // disabled = boolean (default: false)
1049
+ // max = number
1050
+ // size = "sm" | "md" | "lg" (default: md)
1051
+
1052
+ // Examples:
1053
+ const [tags, setTags] = useState<string[]>([]);
1054
+ <TagsInput value={tags} onChange={setTags} placeholder="Add a tag…" />
1055
+ <TagsInput value={tags} onChange={setTags} max={5} />
1056
+ ```
1057
+
1058
+ **Common mistakes:**
1059
+ - ❌ `TagsInput value={string}` → Pass an array: value={['react','vue']}
1060
+ - ❌ `TagsInput without onChange` → Provide value and onChange (React) or v-model (Vue)
1061
+
1062
+ ---
1063
+
1064
+ ### Combobox
1065
+
1066
+ Searchable single-select dropdown with typeahead filtering and keyboard navigation. Use when the list is long enough to need search. Differs from Select (no search) and Command (palette).
1067
+
1068
+ ```tsx
1069
+ import { Combobox } from "@usevyre/react"
1070
+
1071
+ // Props:
1072
+ // options = { value: string; label: string; disabled?: boolean }[]
1073
+ // value = string | null
1074
+ // onChange = (value: string | null) => void
1075
+ // placeholder = string (default: "Search…")
1076
+ // disabled = boolean (default: false)
1077
+ // size = "sm" | "md" | "lg" (default: md)
1078
+ // emptyText = string (default: "No results")
1079
+
1080
+ // Examples:
1081
+ const [lang, setLang] = useState<string | null>(null);
1082
+ <Combobox
1083
+ options={[{ value: "ts", label: "TypeScript" }, { value: "go", label: "Go" }]}
1084
+ value={lang}
1085
+ onChange={setLang}
1086
+ placeholder="Search language…"
1087
+ />
1088
+ ```
1089
+
1090
+ **Common mistakes:**
1091
+ - ❌ `Combobox value=""` → Use value={null} for no selection
1092
+ - ❌ `Combobox options={string[]}` → Use [{ value: 'ts', label: 'TypeScript' }]
1093
+ - ❌ `Using Combobox for command palette` → Use Command for command palettes
1094
+
1095
+ ---
1096
+
1097
+ ### DataGrid
1098
+
1099
+ Table with built-in column sorting, loading skeletons, and empty state. Filtering and pagination are out of scope — compose with the Pagination component.
1100
+
1101
+ ```tsx
1102
+ import { DataGrid } from "@usevyre/react"
1103
+
1104
+ // Props:
1105
+ // columns = { key: string; label: string; sortable?: boolean; width?: string }[]
1106
+ // rows = Record<string, unknown>[]
1107
+ // sortKey = string
1108
+ // sortDir = "asc" | "desc"
1109
+ // onSort = (key: string, dir: 'asc' | 'desc') => void
1110
+ // loading = boolean (default: false)
1111
+ // emptyText = string (default: "No data")
1112
+ // stickyHeader = boolean (default: false)
1113
+
1114
+ // Examples:
1115
+ const cols = [{ key: "name", label: "Name", sortable: true }];
1116
+ <DataGrid
1117
+ columns={cols}
1118
+ rows={people}
1119
+ sortKey={sortKey}
1120
+ sortDir={sortDir}
1121
+ onSort={(k, d) => { setSortKey(k); setSortDir(d); }}
1122
+ />
1123
+ <DataGrid columns={cols} rows={[]} loading />
1124
+ ```
1125
+
1126
+ **Common mistakes:**
1127
+ - ❌ `DataGrid expecting built-in pagination` → Slice rows yourself and use the Pagination component
1128
+ - ❌ `DataGrid expecting built-in filtering` → Filter the rows array before passing it in
1129
+ - ❌ `sortable without onSort` → Handle onSort and sort the rows array in your state
1130
+
1131
+ ---
1132
+
1133
+ ### Tag
1134
+
1135
+ Standalone display tag/chip for categories, labels, or filter chips. NOT an input — for tag input use TagsInput. Group multiple with TagGroup.
1136
+
1137
+ ```tsx
1138
+ import { Tag } from "@usevyre/react"
1139
+
1140
+ // Props:
1141
+ // variant = "default" | "accent" | "danger" (default: default)
1142
+ // size = "sm" | "md" | "lg" (default: md)
1143
+ // onRemove = () => void
1144
+ // onClick = () => void
1145
+ // disabled = boolean (default: false)
1146
+
1147
+ // Examples:
1148
+ <TagGroup>
1149
+ <Tag>Design</Tag>
1150
+ <Tag variant="accent">Featured</Tag>
1151
+ <Tag>Engineering</Tag>
1152
+ </TagGroup>
1153
+ <Tag onRemove={() => removeFilter("react")}>react</Tag>
1154
+ <Tag onClick={() => toggleFilter("vue")}>vue</Tag>
1155
+ ```
1156
+
1157
+ **Common mistakes:**
1158
+ - ❌ `Tag variant="success"` → Use Badge for success/warning/teal status colors; Tag is for categories/filters
1159
+ - ❌ `Using Tag for tag input` → Use TagsInput for adding/removing tags via keyboard
1160
+ - ❌ `Tag size="xl"` → Use size="lg"
1161
+
1162
+ ---
1163
+
1164
+ ### TagGroup
1165
+
1166
+ Read-only container that lays out multiple Tag elements with automatic wrapping and consistent spacing. For tag input use TagsInput.
1167
+
1168
+ ```tsx
1169
+ import { TagGroup, Tag } from "@usevyre/react"
1170
+
1171
+ // Props:
1172
+ // gap = "sm" | "md" | "lg" (default: md)
1173
+ // wrap = boolean (default: true)
1174
+
1175
+ // Examples:
1176
+ <TagGroup gap="sm">
1177
+ <Tag>React</Tag>
1178
+ <Tag>Vue</Tag>
1179
+ <Tag variant="accent">TypeScript</Tag>
1180
+ </TagGroup>
1181
+ ```
1182
+
1183
+ **Common mistakes:**
1184
+ - ❌ `TagGroup without Tag children` → Place <Tag> elements as direct children
1185
+ - ❌ `Using TagGroup for tag input` → Use TagsInput for an editable tag field
1186
+
1187
+ ---
1188
+
1189
+ ### Item
1190
+
1191
+ Layout primitive for list rows, settings rows, and notification rows. Denser than Card — use Item (not Card) for repeated list rows.
1192
+
1193
+ ```tsx
1194
+ import { Item, ItemMedia, ItemContent, ItemTitle, ItemDescription, ItemActions, ItemGroup } from "@usevyre/react"
1195
+
1196
+ // Props:
1197
+ // variant = "default" | "outlined" | "muted" | "plain" (default: default)
1198
+ // size = "sm" | "md" | "lg" (default: md)
1199
+ // clickable = boolean (default: false)
1200
+
1201
+ // Examples:
1202
+ <Item>
1203
+ <ItemMedia><BellIcon /></ItemMedia>
1204
+ <ItemContent>
1205
+ <ItemTitle>Notifications</ItemTitle>
1206
+ <ItemDescription>Receive an email when someone mentions you.</ItemDescription>
1207
+ </ItemContent>
1208
+ <ItemActions>
1209
+ <Switch defaultChecked />
1210
+ </ItemActions>
1211
+ </Item>
1212
+ <ItemGroup separated>
1213
+ <Item clickable>
1214
+ <ItemContent><ItemTitle>Profile</ItemTitle></ItemContent>
1215
+ </Item>
1216
+ <Item clickable>
1217
+ <ItemContent><ItemTitle>Billing</ItemTitle></ItemContent>
1218
+ </Item>
1219
+ </ItemGroup>
1220
+ ```
1221
+
1222
+ **Common mistakes:**
1223
+ - ❌ `Card used for repeated list rows` → Use <Item> (optionally inside <ItemGroup separated>) for list/settings rows
1224
+ - ❌ `Item variant="primary"` → Use variant="default" | "outlined" | "muted"
1225
+ - ❌ `raw text directly inside Item` → Wrap text in <ItemContent><ItemTitle>…</ItemTitle></ItemContent>
1226
+
1227
+ ---
1228
+
1229
+ ### Kanban
1230
+
1231
+ Drag-and-drop board: cards move between columns (or reorder within a column). CONTROLLED & data-driven like DataGrid. While dragging, a placeholder shows the exact drop position. Each card is wrapped in a Card (variant="outlined"); renderCard (React) / #card slot (Vue) can render ANY content incl. complex components (Avatar/Badge/Progress). Columns and cards accept an optional semantic color tint. Native HTML5 DnD, zero deps.
1232
+
1233
+ ```tsx
1234
+ import { Kanban } from "@usevyre/react"
1235
+
1236
+ // Props:
1237
+ // value = KanbanColumn[]
1238
+ // onChange = function
1239
+ // renderCard = function
1240
+ // onCardClick = function
1241
+
1242
+ // Examples:
1243
+ const [columns, setColumns] = useState([
1244
+ { id: "todo", title: "To Do", cards: [{ id: "1", title: "Spec API" }] },
1245
+ { id: "doing", title: "In Progress", cards: [] },
1246
+ { id: "done", title: "Done", cards: [{ id: "2", title: "Kickoff" }] },
1247
+ ]);
1248
+ <Kanban value={columns} onChange={setColumns} />
1249
+ <Kanban
1250
+ value={columns}
1251
+ onChange={setColumns}
1252
+ onCardClick={(card) => openDetail(card.id)}
1253
+ renderCard={(card) => (
1254
+ <><strong>{card.title}</strong><Badge>{card.id}</Badge></>
1255
+ )}
1256
+ />
1257
+ const [cols, setCols] = useState([
1258
+ { id: "doing", title: "In Progress", color: "teal", cards: [
1259
+ { id: "t1", title: "OAuth", assignee: "AK", progress: 60, color: "warning" },
1260
+ ]},
1261
+ ]);
1262
+ <Kanban
1263
+ value={cols}
1264
+ onChange={setCols}
1265
+ renderCard={(card) => (
1266
+ <><strong>{card.title}</strong><Progress value={card.progress} /></>
1267
+ )}
1268
+ />
1269
+ ```
1270
+
1271
+ **Common mistakes:**
1272
+ - ❌ `Kanban without onChange (or ignoring it)` → Store columns in state and setColumns in onChange (v-model in Vue)
1273
+ - ❌ `Duplicate card ids across columns` → Use globally-unique card ids across the entire board
1274
+ - ❌ `Mutating value in place then calling onChange` → Pass the new array Kanban gives you straight to setState / v-model
1275
+ - ❌ `color="blue" (or any non-semantic value)` → Use one of: "default" | "accent" | "teal" | "success" | "warning" | "danger"
1276
+
1277
+ ---
1278
+
1279
+ ### Conversation
1280
+
1281
+ Chat / inbox message thread. CONTROLLED & data-driven like Kanban — you own `value` (messages array) and append in your own send handler; Conversation holds no message state. Consecutive messages from the same author are grouped (avatar + name shown once), day separators are inserted on date change, and outgoing messages (authorId === currentUserId) align right.
1282
+
1283
+ ```tsx
1284
+ import { Conversation } from "@usevyre/react"
1285
+
1286
+ // Props:
1287
+ // value = ConversationMessage[]
1288
+ // currentUserId = string
1289
+ // composer = boolean (default: false)
1290
+ // onSend = function
1291
+ // placeholder = string (default: Write a message…)
1292
+ // typing = boolean | string (default: false)
1293
+ // allowAttachments = boolean (default: false)
1294
+ // accept = string
1295
+ // renderMessage = function
1296
+ // renderComposer = function
1297
+
1298
+ // Examples:
1299
+ const [messages, setMessages] = useState([
1300
+ { id: "1", authorId: "sam", authorName: "Sam", text: "Hey!" },
1301
+ { id: "2", authorId: "me", text: "Hi \ud83d\udc4b", status: "read" },
1302
+ ]);
1303
+ <Conversation
1304
+ value={messages}
1305
+ currentUserId="me"
1306
+ composer
1307
+ onSend={(t) => setMessages((m) => [...m, { id: crypto.randomUUID(), authorId: "me", text: t }])}
1308
+ />
1309
+ <Conversation
1310
+ value={messages}
1311
+ currentUserId="me"
1312
+ typing="Sam is typing"
1313
+ renderMessage={(m) => <strong>{m.text}</strong>}
1314
+ />
1315
+ const messages = [
1316
+ { id: "1", authorId: "sam", authorName: "Sam", text: "Moodboard \ud83d\udc47",
1317
+ attachments: [{ kind: "image", url: "/board.png", name: "board.png" }] },
1318
+ { id: "2", authorId: "me", text: "Specs:", status: "read",
1319
+ attachments: [{ kind: "file", url: "/spec.pdf", name: "spec.pdf", size: "2.4 MB" }] },
1320
+ ];
1321
+ <Conversation value={messages} currentUserId="me" />
1322
+ ```
1323
+
1324
+ **Common mistakes:**
1325
+ - ❌ `Conversation without currentUserId` → Always pass currentUserId matching one of the message authorId values
1326
+ - ❌ `Expecting Conversation to store/append messages` → Append to your own state in onSend (or @send) and pass it back via value
1327
+ - ❌ `composer without onSend (React) / @send (Vue)` → Provide onSend / @send to append the message to value
1328
+ - ❌ `Treating onSend as (text) only when using allowAttachments` → Handle onSend(text, files) — map files to message attachments and append
1329
+
1330
+ ---
1331
+
1332
+ ### DateRangePicker
1333
+
1334
+ Start/end date range picker. Built on Calendar (mode=range) with a friendlier { from, to } object API, a two-month side-by-side view, and preset shortcuts. Use this for report/filter date ranges; use DatePicker for a single date.
1335
+
1336
+ ```tsx
1337
+ import { DateRangePicker } from "@usevyre/react"
1338
+
1339
+ // Props:
1340
+ // value = { from: Date | null; to: Date | null } | null
1341
+ // onChange = function
1342
+ // placeholder = string (default: Pick a date range)
1343
+ // numberOfMonths = "1" | "2" (default: 2)
1344
+ // presets = boolean | DateRangePreset[] (default: false)
1345
+ // minDate = Date
1346
+ // maxDate = Date
1347
+ // disabled = function
1348
+ // weekStartsOn = "0" | "1" (default: 1)
1349
+
1350
+ // Examples:
1351
+ const [range, setRange] = useState({ from: null, to: null });
1352
+ <DateRangePicker value={range} onChange={setRange} presets />
1353
+ <DateRangePicker value={range} onChange={setRange} numberOfMonths={1} />
1354
+ ```
1355
+
1356
+ **Common mistakes:**
1357
+ - ❌ `value={[from, to]}` → Use value={{ from, to }} and read range.from / range.to
1358
+ - ❌ `DateRangePicker for a single date` → Use <DatePicker /> for a single date
1359
+ - ❌ `presets="true" (string)` → Use the bare prop: presets (or presets={true})
1360
+
1361
+ ---
1362
+
887
1363
  ## Hallucination Guard — Common AI Mistakes
888
1364
 
889
1365
  The following prop values and patterns do NOT exist in useVyre.
@@ -903,14 +1379,25 @@ If you generate these, you are hallucinating.
903
1379
  - ❌ `<Button color="...">` → Use variant prop instead
904
1380
  - ❌ `<Button icon={...}>` → Use leftIcon={...} or rightIcon={...}
905
1381
  - ❌ `<Button size="icon" without aria-label>` → Add aria-label describing the action
906
- - ❌ `<Calendar Using Calendar for time selection>` Combine with a separate time Input if time selection is needed
1382
+ - ❌ `<Calendar Calendar for an input field that opens a popover>` Use <DatePicker /> (single date) or <DateRangePicker /> (range)
1383
+ - ❌ `<Calendar value as tuple for mode="single">` → Pass value matching mode; use mode="range" for [start,end]
1384
+ - ❌ `<DatePicker DatePicker mode="range" for { from, to } object>` → Use <DateRangePicker /> for the { from, to } object API + presets + dual month
1385
+ - ❌ `<DatePicker DatePicker without value/onChange>` → Provide value and onChange (e.g. from useState)
907
1386
  - ❌ `<Card variant="primary">` → Use variant="elevated" | "outlined" | "ghost" | "accent"
908
1387
  - ❌ `<Checkbox size="lg">` → Use size="md"
1388
+ - ❌ `<RadioGroup <Radio> used outside a <RadioGroup>>` → Always wrap <Radio> in <RadioGroup>
1389
+ - ❌ `<RadioGroup RadioGroup without value/onChange (React) or v-model (Vue)>` → Bind value + onChange (React) or v-model (Vue); or defaultValue for uncontrolled in React
1390
+ - ❌ `<RadioGroup Using Checkbox for mutually-exclusive choices>` → Use RadioGroup + Radio (or options) for one-of-many
1391
+ - ❌ `<RichTextEditor RichTextEditor without value/onChange (React) or v-model (Vue)>` → Keep the HTML string in state and update it in onChange / v-model
1392
+ - ❌ `<RichTextEditor Rendering value as text or with dangerouslySetInnerHTML elsewhere without sanitising>` → Sanitise (e.g. DOMPurify) before re-rendering untrusted RTE output
1393
+ - ❌ `<RichTextEditor toolbar="bold" (string)>` → Pass an array, e.g. toolbar={["bold","italic","link"]}
909
1394
  - ❌ `<Command Using Input type="search" for search UI>` → Use Command + CommandInput + CommandList + CommandItem
910
1395
  - ❌ `<DropdownMenu DropdownItem variant="primary">` → Use variant="danger" for destructive items only
911
1396
  - ❌ `<Field Applying state prop directly to Input>` → Wrap Input in <Field state="error"> to apply validation styling
1397
+ - ❌ `<Field Mixing props label/hint AND FieldLabel/FieldError for the same field>` → Pick one: either props-based (label/hint/state) OR composable parts
912
1398
  - ❌ `<Input size="icon">` → Use size="sm" | "md" | "lg"
913
1399
  - ❌ `<Input type="search" for search UI>` → Import Command from @usevyre/react for search palettes
1400
+ - ❌ `<Input Vue: binding Input/Textarea value without v-model>` → Use v-model on <Input>/<Textarea> in Vue; in React use value + onChange
914
1401
  - ❌ `<Modal size="xl">` → Use size="lg" or size="full"
915
1402
  - ❌ `<Popover placement="top-center">` → Use placement="top" for centered placement
916
1403
  - ❌ `<Progress value > 100>` → Normalize your value to 0–100 range before passing
@@ -920,6 +1407,35 @@ If you generate these, you are hallucinating.
920
1407
  - ❌ `<Toast variant="info">` → Use variant="default"
921
1408
  - ❌ `<Tooltip Using Tooltip for rich content (forms, buttons, etc.)>` → Use Popover for rich interactive content
922
1409
  - ❌ `<Typography Using raw <h1>, <p> tags instead of Typography components>` → Use <Heading>, <Text>, <Lead> from @usevyre/react
1410
+ - ❌ `<ButtonGroup ButtonGroup variant="...">` → Set variant on each <Button> inside the group
1411
+ - ❌ `<ButtonGroup ButtonGroup without Button children>` → Place <Button> elements as direct children
1412
+ - ❌ `<TagsInput TagsInput value={string}>` → Pass an array: value={['react','vue']}
1413
+ - ❌ `<TagsInput TagsInput without onChange>` → Provide value and onChange (React) or v-model (Vue)
1414
+ - ❌ `<Combobox Combobox value="">` → Use value={null} for no selection
1415
+ - ❌ `<Combobox Combobox options={string[]}>` → Use [{ value: 'ts', label: 'TypeScript' }]
1416
+ - ❌ `<Combobox Using Combobox for command palette>` → Use Command for command palettes
1417
+ - ❌ `<DataGrid DataGrid expecting built-in pagination>` → Slice rows yourself and use the Pagination component
1418
+ - ❌ `<DataGrid DataGrid expecting built-in filtering>` → Filter the rows array before passing it in
1419
+ - ❌ `<DataGrid sortable without onSort>` → Handle onSort and sort the rows array in your state
1420
+ - ❌ `<Tag Tag variant="success">` → Use Badge for success/warning/teal status colors; Tag is for categories/filters
1421
+ - ❌ `<Tag Using Tag for tag input>` → Use TagsInput for adding/removing tags via keyboard
1422
+ - ❌ `<Tag Tag size="xl">` → Use size="lg"
1423
+ - ❌ `<TagGroup TagGroup without Tag children>` → Place <Tag> elements as direct children
1424
+ - ❌ `<TagGroup Using TagGroup for tag input>` → Use TagsInput for an editable tag field
1425
+ - ❌ `<Item Card used for repeated list rows>` → Use <Item> (optionally inside <ItemGroup separated>) for list/settings rows
1426
+ - ❌ `<Item Item variant="primary">` → Use variant="default" | "outlined" | "muted"
1427
+ - ❌ `<Item raw text directly inside Item>` → Wrap text in <ItemContent><ItemTitle>…</ItemTitle></ItemContent>
1428
+ - ❌ `<Kanban Kanban without onChange (or ignoring it)>` → Store columns in state and setColumns in onChange (v-model in Vue)
1429
+ - ❌ `<Kanban Duplicate card ids across columns>` → Use globally-unique card ids across the entire board
1430
+ - ❌ `<Kanban Mutating value in place then calling onChange>` → Pass the new array Kanban gives you straight to setState / v-model
1431
+ - ❌ `<Kanban color="blue" (or any non-semantic value)>` → Use one of: "default" | "accent" | "teal" | "success" | "warning" | "danger"
1432
+ - ❌ `<Conversation Conversation without currentUserId>` → Always pass currentUserId matching one of the message authorId values
1433
+ - ❌ `<Conversation Expecting Conversation to store/append messages>` → Append to your own state in onSend (or @send) and pass it back via value
1434
+ - ❌ `<Conversation composer without onSend (React) / @send (Vue)>` → Provide onSend / @send to append the message to value
1435
+ - ❌ `<Conversation Treating onSend as (text) only when using allowAttachments>` → Handle onSend(text, files) — map files to message attachments and append
1436
+ - ❌ `<DateRangePicker value={[from, to]}>` → Use value={{ from, to }} and read range.from / range.to
1437
+ - ❌ `<DateRangePicker DateRangePicker for a single date>` → Use <DatePicker /> for a single date
1438
+ - ❌ `<DateRangePicker presets="true" (string)>` → Use the bare prop: presets (or presets={true})
923
1439
 
924
1440
  ---
925
1441
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usevyre/ai-context",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "useVyre AI context — inject into LLM system prompts to eliminate UI hallucinations",
5
5
  "keywords": [
6
6
  "design-system",