@treeui/mcp 0.2.0 → 0.4.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/index.cjs CHANGED
@@ -47,9 +47,9 @@ module.exports = __toCommonJS(index_exports);
47
47
 
48
48
  // src/generated/treeui.catalog.json
49
49
  var treeui_catalog_default = {
50
- generatedAt: "2026-06-26T22:57:20.487Z",
50
+ generatedAt: "2026-07-18T01:52:38.578Z",
51
51
  packageName: "@treeui/vue",
52
- packageVersion: "0.8.0",
52
+ packageVersion: "0.14.0",
53
53
  decisionGuides: [
54
54
  {
55
55
  id: "single-choice",
@@ -247,6 +247,50 @@ var treeui_catalog_default = {
247
247
  notes: []
248
248
  }
249
249
  ]
250
+ },
251
+ {
252
+ id: "data-viz-choice",
253
+ question: "Visualize numeric data",
254
+ options: [
255
+ {
256
+ use: "TStat",
257
+ when: [
258
+ "The story is a single headline number, optionally with a trend and delta.",
259
+ "It is not a plot \u2014 prefer the number over a chart when one value says it."
260
+ ],
261
+ notes: []
262
+ },
263
+ {
264
+ use: "TSparkline",
265
+ when: [
266
+ "You want an inline micro-trend next to a value, in a table cell, or in text.",
267
+ "No axes, legend, or tooltip are needed."
268
+ ],
269
+ notes: []
270
+ },
271
+ {
272
+ use: "TChart",
273
+ when: [
274
+ "You need line, area, or bar plots with axes, gridlines, legend, and hover tooltip.",
275
+ "There are one or more series over shared categories."
276
+ ],
277
+ notes: []
278
+ },
279
+ {
280
+ use: "TDonutChart",
281
+ when: [
282
+ "The data is a part-of-whole breakdown (donut or pie)."
283
+ ],
284
+ notes: []
285
+ },
286
+ {
287
+ use: "TProgress",
288
+ when: [
289
+ "You are showing completion toward a single target, not a distribution or trend."
290
+ ],
291
+ notes: []
292
+ }
293
+ ]
250
294
  }
251
295
  ],
252
296
  recipes: [
@@ -296,11 +340,28 @@ var treeui_catalog_default = {
296
340
  "Set end `min` from the selected start time when appropriate."
297
341
  ]
298
342
  },
343
+ {
344
+ id: "app-shell",
345
+ name: "Full-viewport application shell",
346
+ useWhen: [
347
+ "The app needs a header plus side navigation that fills the whole screen and adapts to mobile.",
348
+ "The layout must use the full page width on large monitors, not a centered column."
349
+ ],
350
+ compose: [
351
+ "TAppShell",
352
+ "TNavMenu"
353
+ ],
354
+ notes: [
355
+ "`TAppShell` orchestrates the header, sidebar, and fluid content in one component; put a `TNavMenu` in its `sidebar` slot and it inherits collapsed state automatically.",
356
+ "Leave `mobile` unset to auto-switch at `breakpoint` (default 768px), or bind it to force mobile; the sidebar becomes an off-canvas drawer with a header menu button.",
357
+ "Mount it at the app root. Wrap only inner regions in `TContainer` if a specific area needs a constrained reading width."
358
+ ]
359
+ },
299
360
  {
300
361
  id: "sidebar-shell",
301
362
  name: "Sidebar application shell",
302
363
  useWhen: [
303
- "The screen needs persistent section navigation."
364
+ "The screen needs persistent section navigation and you are composing the shell by hand."
304
365
  ],
305
366
  compose: [
306
367
  "TSidebar",
@@ -309,7 +370,8 @@ var treeui_catalog_default = {
309
370
  "TContainer"
310
371
  ],
311
372
  notes: [
312
- "Let `TNavMenu` inherit collapsed state from `TSidebar` when possible."
373
+ "Let `TNavMenu` inherit collapsed state from `TSidebar` when possible.",
374
+ "Prefer `TAppShell` (recipe `app-shell`) when you also need full-viewport sizing and a responsive mobile drawer."
313
375
  ]
314
376
  },
315
377
  {
@@ -407,15 +469,38 @@ var treeui_catalog_default = {
407
469
  id: "chart-surface",
408
470
  name: "Chart surface",
409
471
  useWhen: [
410
- "A screen needs data visualization, but the charting engine should stay external to TreeUI."
472
+ "A screen needs data visualization inside a framed, titled surface."
411
473
  ],
412
474
  compose: [
413
475
  "TCard",
414
476
  "TStack",
415
- "external chart library"
477
+ "TChart",
478
+ "TSelect"
416
479
  ],
417
480
  notes: [
418
- "Use TreeUI for the surrounding surface, headings, badges, filters, and empty/loading states."
481
+ "Prefer the native TChart / TDonutChart / TSparkline \u2014 no external charting library is required.",
482
+ "Use TreeUI for the surrounding surface, headings, badges, range filters, and empty/loading states.",
483
+ "TChart has its own loading and empty slots; you can still wrap it in TCard for the frame.",
484
+ "Only reach for an external chart engine for chart types TreeUI does not cover yet (e.g. scatter, heatmap)."
485
+ ]
486
+ },
487
+ {
488
+ id: "metric-dashboard",
489
+ name: "Metric dashboard",
490
+ useWhen: [
491
+ "A dashboard shows KPIs alongside trends and breakdowns."
492
+ ],
493
+ compose: [
494
+ "TStat",
495
+ "TSparkline",
496
+ "TChart",
497
+ "TDonutChart",
498
+ "TGrid"
499
+ ],
500
+ notes: [
501
+ "Lead with TStat tiles; embed a TSparkline in a stat's meta slot for the inline trend.",
502
+ "Use one TChart for the main time series and a TDonutChart for a part-of-whole split.",
503
+ "Keep series colors stable across the dashboard \u2014 rely on the --tree-color-chart-* order, do not remap by rank."
419
504
  ]
420
505
  },
421
506
  {
@@ -565,17 +650,27 @@ var treeui_catalog_default = {
565
650
  "Use `accept`, `multiple`, and size limits in the app layer to constrain uploads."
566
651
  ]
567
652
  },
653
+ TAppShell: {
654
+ required: [],
655
+ notes: [
656
+ "Mount at the app root; it fills the viewport (100dvh, full width) and scrolls its content region independently.",
657
+ "Put navigation in the `sidebar` slot (e.g. `TNavMenu`) \u2014 it inherits collapsed state from the shell provider like `TSidebar`.",
658
+ "Leave `mobile` unset to auto-switch to an off-canvas drawer at `breakpoint`, or bind `mobile` to control it explicitly.",
659
+ "Use `v-model:collapsed` for the desktop rail and `v-model:sidebarOpen` for the mobile drawer when the app persists layout state."
660
+ ]
661
+ },
568
662
  TSidebar: {
569
663
  required: [],
570
664
  notes: [
571
665
  "Pair with `TNavMenu` for app-section navigation.",
572
- "Use controlled `collapsed` state when the app shell persists layout preferences."
666
+ "Use controlled `collapsed` state when the app shell persists layout preferences.",
667
+ "Prefer `TAppShell` when you also want full-viewport sizing, a top header, and a responsive mobile drawer."
573
668
  ]
574
669
  },
575
670
  TNavMenu: {
576
671
  required: [],
577
672
  notes: [
578
- "When nested inside `TSidebar`, `collapsed` can be inherited from the sidebar provider."
673
+ "When nested inside `TSidebar` or `TAppShell`, `collapsed` can be inherited from the sidebar provider."
579
674
  ]
580
675
  },
581
676
  TTreeView: {
@@ -609,6 +704,32 @@ var treeui_catalog_default = {
609
704
  "Use `href` for native links.",
610
705
  "Use `to` when the app has vue-router and breadcrumb items should render RouterLink."
611
706
  ]
707
+ },
708
+ TChart: {
709
+ required: [],
710
+ notes: [
711
+ "Pass `series` as `{ name, data, color? }[]` and `labels` as a parallel `string[]` (one label per data index).",
712
+ "Set `type` to `line`, `area`, or `bar`; use `stacked` only with `bar`.",
713
+ "Series colors come from `--tree-color-chart-1..8` in order; pass `color` per series to override \u2014 do not remap colors when the series set changes.",
714
+ "Pass `valueFormat: (n) => string` to format axis and tooltip values (currency, compact, etc.).",
715
+ "No charting library or provider is required \u2014 the SVG and geometry are built in."
716
+ ]
717
+ },
718
+ TSparkline: {
719
+ required: [],
720
+ notes: [
721
+ "Pass `data` as `number[]`; there are no axes, legend, or tooltip by design.",
722
+ "Size it with `width`/`height`; use it inside `TStat`, table cells, or text.",
723
+ "Always keep a visible number nearby \u2014 a sparkline supplements a value, it does not replace it."
724
+ ]
725
+ },
726
+ TDonutChart: {
727
+ required: [],
728
+ notes: [
729
+ "Pass `segments` as `{ label, value, color? }[]`; non-positive values are dropped.",
730
+ "Set `thickness: 0` for a full pie; keep the default for a donut with a center readout.",
731
+ "Use the `center` slot (donut only) for a custom center; otherwise it shows the total or the hovered slice."
732
+ ]
612
733
  }
613
734
  },
614
735
  appPatterns: {
@@ -622,6 +743,9 @@ var treeui_catalog_default = {
622
743
  commonCompositions: [
623
744
  "TFormField + input-like control",
624
745
  "TGrid + TStat",
746
+ "TStat + TSparkline",
747
+ "TCard + TChart",
748
+ "TAppShell + TNavMenu",
625
749
  "TSidebar + TNavMenu",
626
750
  "TTable + TPagination",
627
751
  "TPricing + TPricingCard"
@@ -782,6 +906,211 @@ var treeui_catalog_default = {
782
906
  },
783
907
  recipes: []
784
908
  },
909
+ {
910
+ name: "TAppShell",
911
+ aliases: [],
912
+ package: "@treeui/vue",
913
+ kind: "layout",
914
+ category: "Layout",
915
+ status: "available",
916
+ purpose: "Full-viewport application shell with a top header, a persistent side navigation, and a fluid content area that collapses the sidebar into an off-canvas drawer on small screens.",
917
+ sourceFile: "packages/vue/src/components/TAppShell.vue",
918
+ storyFile: "apps/docs/src/stories/AppShell.stories.ts",
919
+ storyTitle: "Components/Layout/AppShell",
920
+ manifestFile: "docs/ai/COMPONENTS/app-shell.yaml",
921
+ props: [
922
+ {
923
+ name: "as",
924
+ type: "string",
925
+ default: "div"
926
+ },
927
+ {
928
+ name: "side",
929
+ type: "TSidebarSide",
930
+ default: "left",
931
+ values: [
932
+ "left",
933
+ "right"
934
+ ]
935
+ },
936
+ {
937
+ name: "sidebarWidth",
938
+ type: "string",
939
+ default: "17.5rem"
940
+ },
941
+ {
942
+ name: "collapsedWidth",
943
+ type: "string",
944
+ default: "4.75rem"
945
+ },
946
+ {
947
+ name: "collapsible",
948
+ type: "boolean",
949
+ default: "false"
950
+ },
951
+ {
952
+ name: "collapsed",
953
+ type: "boolean",
954
+ default: "undefined"
955
+ },
956
+ {
957
+ name: "defaultCollapsed",
958
+ type: "boolean",
959
+ default: "false"
960
+ },
961
+ {
962
+ name: "mobile",
963
+ type: "boolean",
964
+ default: "undefined"
965
+ },
966
+ {
967
+ name: "breakpoint",
968
+ type: "string",
969
+ default: "768px"
970
+ },
971
+ {
972
+ name: "sidebarOpen",
973
+ type: "boolean",
974
+ default: "undefined"
975
+ },
976
+ {
977
+ name: "defaultSidebarOpen",
978
+ type: "boolean",
979
+ default: "false"
980
+ },
981
+ {
982
+ name: "closeOnEscape",
983
+ type: "boolean",
984
+ default: "true"
985
+ },
986
+ {
987
+ name: "closeOnOverlay",
988
+ type: "boolean",
989
+ default: "true"
990
+ },
991
+ {
992
+ name: "showMenuButton",
993
+ type: "boolean",
994
+ default: "true"
995
+ },
996
+ {
997
+ name: "menuLabel",
998
+ type: "string",
999
+ default: "Open menu"
1000
+ },
1001
+ {
1002
+ name: "closeLabel",
1003
+ type: "string",
1004
+ default: "Close menu"
1005
+ },
1006
+ {
1007
+ name: "sidebarLabel",
1008
+ type: "string",
1009
+ default: "Sidebar"
1010
+ }
1011
+ ],
1012
+ events: [
1013
+ {
1014
+ name: "update:collapsed"
1015
+ },
1016
+ {
1017
+ name: "collapse-change"
1018
+ },
1019
+ {
1020
+ name: "update:sidebarOpen"
1021
+ },
1022
+ {
1023
+ name: "sidebar-open-change"
1024
+ }
1025
+ ],
1026
+ slots: [
1027
+ {
1028
+ name: "header"
1029
+ },
1030
+ {
1031
+ name: "sidebar"
1032
+ },
1033
+ {
1034
+ name: "default"
1035
+ },
1036
+ {
1037
+ name: "menu-icon"
1038
+ }
1039
+ ],
1040
+ a11y: [
1041
+ "role: dialog",
1042
+ "aria_modal: true",
1043
+ "focus_trap: true",
1044
+ "restores_focus_on_close: true",
1045
+ "escape_closes_when_enabled: true",
1046
+ "menu_button_aria_expanded: true",
1047
+ "menu_button_aria_controls_drawer: true",
1048
+ "sidebar_labelled_by_sidebar_label: true"
1049
+ ],
1050
+ behavior: [
1051
+ "fills_viewport_full_width_and_height: true",
1052
+ "header_spans_full_width_above_sidebar_and_content: true",
1053
+ "content_area_scrolls_independently: true",
1054
+ "auto_switches_to_mobile_at_breakpoint_when_mobile_undefined: true",
1055
+ "mobile_prop_forces_mobile_mode: true",
1056
+ "mobile_sidebar_becomes_off_canvas_drawer: true",
1057
+ "auto_menu_button_in_header_on_mobile: true",
1058
+ "controlled_and_uncontrolled_collapsed_state: true",
1059
+ "controlled_and_uncontrolled_sidebar_open_state: true",
1060
+ "provides_collapsed_state_to_descendants: true",
1061
+ "drawer_reports_collapsed_false_to_descendants_and_slots: true",
1062
+ "left_and_right_variants: true",
1063
+ "locks_body_scroll_while_drawer_open: true",
1064
+ "overlay_click_closes_when_enabled: true",
1065
+ "exposes_slot_props_with_mobile_collapsed_and_toggles: true"
1066
+ ],
1067
+ selection: {
1068
+ keywords: [
1069
+ "app shell",
1070
+ "layout",
1071
+ "dashboard",
1072
+ "admin",
1073
+ "header sidebar",
1074
+ "full screen",
1075
+ "responsive shell",
1076
+ "off-canvas"
1077
+ ],
1078
+ useWhen: [
1079
+ "You need a full-viewport application layout with a header, a side navigation, and a fluid content area.",
1080
+ "The layout should use the entire page width (not a centered max-width) and switch the sidebar to an off-canvas drawer on small screens."
1081
+ ],
1082
+ avoidWhen: [
1083
+ "You only need one region (just a top bar, or just a side rail) \u2014 compose TNavbar or TSidebar directly.",
1084
+ "The page content should be centered with a readable max width \u2014 use TContainer inside the shell content instead."
1085
+ ],
1086
+ alternatives: [
1087
+ {
1088
+ component: "TSidebar",
1089
+ when: "You want a standalone collapsible side rail without the header/content orchestration or responsive drawer."
1090
+ },
1091
+ {
1092
+ component: "TNavbar",
1093
+ when: "You only need the top application bar."
1094
+ },
1095
+ {
1096
+ component: "TDrawer",
1097
+ when: "You need a general temporary overlay panel rather than the shell's responsive navigation."
1098
+ }
1099
+ ]
1100
+ },
1101
+ setup: {
1102
+ required: [],
1103
+ notes: [
1104
+ "Mount at the app root; it fills the viewport (100dvh, full width) and scrolls its content region independently.",
1105
+ "Put navigation in the `sidebar` slot (e.g. `TNavMenu`) \u2014 it inherits collapsed state from the shell provider like `TSidebar`.",
1106
+ "Leave `mobile` unset to auto-switch to an off-canvas drawer at `breakpoint`, or bind `mobile` to control it explicitly.",
1107
+ "Use `v-model:collapsed` for the desktop rail and `v-model:sidebarOpen` for the mobile drawer when the app persists layout state."
1108
+ ]
1109
+ },
1110
+ recipes: [
1111
+ "app-shell"
1112
+ ]
1113
+ },
785
1114
  {
786
1115
  name: "TAvatar",
787
1116
  aliases: [],
@@ -1005,15 +1334,17 @@ var treeui_catalog_default = {
1005
1334
  },
1006
1335
  {
1007
1336
  name: "variant",
1008
- type: "TVariant",
1337
+ type: "TVariant | brand",
1009
1338
  default: "solid",
1010
1339
  values: [
1011
1340
  "solid",
1012
1341
  "outline",
1013
1342
  "ghost",
1014
1343
  "soft",
1015
- "danger"
1016
- ]
1344
+ "danger",
1345
+ "brand"
1346
+ ],
1347
+ note: "`brand` is a button-only, high-emphasis gradient variant driven by the `--tree-gradient-brand` token. Use it for the single primary CTA of a screen."
1017
1348
  },
1018
1349
  {
1019
1350
  name: "size",
@@ -1143,7 +1474,8 @@ var treeui_catalog_default = {
1143
1474
  ],
1144
1475
  a11y: [],
1145
1476
  behavior: [
1146
- "body_wrapper_class: tree-card__body"
1477
+ "body_wrapper_class: t-card__body",
1478
+ "solid_variant_rescopes_color_tokens_for_nested_content: true"
1147
1479
  ],
1148
1480
  selection: {
1149
1481
  keywords: [
@@ -1178,6 +1510,202 @@ var treeui_catalog_default = {
1178
1510
  "chart-surface"
1179
1511
  ]
1180
1512
  },
1513
+ {
1514
+ name: "TChart",
1515
+ aliases: [],
1516
+ package: "@treeui/vue",
1517
+ kind: "data-display",
1518
+ category: "Data Display",
1519
+ status: "available",
1520
+ purpose: "Lightweight cartesian chart (line, area, bar) for multi-series data, with axes, gridlines, hover tooltip, legend, and an accessible data table. No external charting dependency.",
1521
+ sourceFile: "packages/vue/src/components/TChart.vue",
1522
+ storyFile: "apps/docs/src/stories/Chart.stories.ts",
1523
+ storyTitle: "Components/Data Display/Chart",
1524
+ manifestFile: "docs/ai/COMPONENTS/chart.yaml",
1525
+ props: [
1526
+ {
1527
+ name: "type",
1528
+ type: "TChartType",
1529
+ default: "line",
1530
+ values: [
1531
+ "line",
1532
+ "area",
1533
+ "bar"
1534
+ ]
1535
+ },
1536
+ {
1537
+ name: "series",
1538
+ type: "TChartSeries[]",
1539
+ default: "[]",
1540
+ note: "Each item is { name: string; data: number[]; color?: string }."
1541
+ },
1542
+ {
1543
+ name: "labels",
1544
+ type: "string[]",
1545
+ default: "[]",
1546
+ note: "X-axis categories, aligned index-for-index to each series' data."
1547
+ },
1548
+ {
1549
+ name: "height",
1550
+ type: "number",
1551
+ default: "260",
1552
+ note: "Width is fluid (measured via ResizeObserver)."
1553
+ },
1554
+ {
1555
+ name: "smooth",
1556
+ type: "boolean",
1557
+ default: "false",
1558
+ note: "Curved line/area."
1559
+ },
1560
+ {
1561
+ name: "stacked",
1562
+ type: "boolean",
1563
+ default: "false",
1564
+ note: "Stacks bar series; ignored for line/area. Negative values are clamped to 0 in stacked mode (grouped mode renders them)."
1565
+ },
1566
+ {
1567
+ name: "showGrid",
1568
+ type: "boolean",
1569
+ default: "true"
1570
+ },
1571
+ {
1572
+ name: "showLegend",
1573
+ type: "boolean",
1574
+ default: "auto (true when more than one series)"
1575
+ },
1576
+ {
1577
+ name: "showXAxis",
1578
+ type: "boolean",
1579
+ default: "true"
1580
+ },
1581
+ {
1582
+ name: "showYAxis",
1583
+ type: "boolean",
1584
+ default: "true"
1585
+ },
1586
+ {
1587
+ name: "showTooltip",
1588
+ type: "boolean",
1589
+ default: "true"
1590
+ },
1591
+ {
1592
+ name: "showPoints",
1593
+ type: "boolean",
1594
+ default: "false",
1595
+ note: "Always draw markers on line/area points."
1596
+ },
1597
+ {
1598
+ name: "yTicks",
1599
+ type: "number",
1600
+ default: "5"
1601
+ },
1602
+ {
1603
+ name: "valueFormat",
1604
+ type: "(value: number) => string",
1605
+ default: "locale integers for |value| >= 1000"
1606
+ },
1607
+ {
1608
+ name: "minY",
1609
+ type: "number",
1610
+ default: "auto"
1611
+ },
1612
+ {
1613
+ name: "maxY",
1614
+ type: "number",
1615
+ default: "auto"
1616
+ },
1617
+ {
1618
+ name: "animated",
1619
+ type: "boolean",
1620
+ default: "true",
1621
+ note: "Draw-in animation; disabled under prefers-reduced-motion."
1622
+ },
1623
+ {
1624
+ name: "loading",
1625
+ type: "boolean",
1626
+ default: "false"
1627
+ },
1628
+ {
1629
+ name: "ariaLabel",
1630
+ type: "string",
1631
+ default: "auto-generated summary"
1632
+ }
1633
+ ],
1634
+ events: [
1635
+ {
1636
+ name: "point-click",
1637
+ payload: "{ index: number; label: string; values: { name: string; value: number; color: string }[] }"
1638
+ }
1639
+ ],
1640
+ slots: [
1641
+ {
1642
+ name: "legend"
1643
+ },
1644
+ {
1645
+ name: "tooltip"
1646
+ },
1647
+ {
1648
+ name: "empty"
1649
+ }
1650
+ ],
1651
+ a11y: [
1652
+ "SVG is aria-hidden; a visually-hidden <table> mirrors the series data for assistive tech.",
1653
+ "Legend defaults on for multiple series so identity is not color-only.",
1654
+ "Provide a meaningful ariaLabel describing what the chart shows."
1655
+ ],
1656
+ behavior: [
1657
+ "colors_auto_assign_from_chart_palette: --tree-color-chart-1..8, cycling; per-series color overrides",
1658
+ "bar_zero_baseline: true",
1659
+ "line_domain_uses_nice_ticks: true",
1660
+ "hover_shows_crosshair_and_tooltip: true"
1661
+ ],
1662
+ selection: {
1663
+ keywords: [
1664
+ "chart",
1665
+ "graph",
1666
+ "line chart",
1667
+ "area chart",
1668
+ "bar chart",
1669
+ "plot",
1670
+ "dashboard",
1671
+ "time series",
1672
+ "analytics"
1673
+ ],
1674
+ useWhen: [
1675
+ "You need a line, area, or bar chart with axes, gridlines, legend, and a hover tooltip.",
1676
+ "One or more series share a set of category labels."
1677
+ ],
1678
+ avoidWhen: [
1679
+ "A single value tells the story \u2014 use TStat.",
1680
+ "You only need an inline trend with no axes \u2014 use TSparkline.",
1681
+ "The data is a part-of-whole breakdown \u2014 use TDonutChart."
1682
+ ],
1683
+ alternatives: [
1684
+ {
1685
+ component: "TSparkline",
1686
+ when: "The trend should be inline and axis-less."
1687
+ },
1688
+ {
1689
+ component: "TDonutChart",
1690
+ when: "The data is proportional (parts of a whole)."
1691
+ }
1692
+ ]
1693
+ },
1694
+ setup: {
1695
+ required: [],
1696
+ notes: [
1697
+ "Pass `series` as `{ name, data, color? }[]` and `labels` as a parallel `string[]` (one label per data index).",
1698
+ "Set `type` to `line`, `area`, or `bar`; use `stacked` only with `bar`.",
1699
+ "Series colors come from `--tree-color-chart-1..8` in order; pass `color` per series to override \u2014 do not remap colors when the series set changes.",
1700
+ "Pass `valueFormat: (n) => string` to format axis and tooltip values (currency, compact, etc.).",
1701
+ "No charting library or provider is required \u2014 the SVG and geometry are built in."
1702
+ ]
1703
+ },
1704
+ recipes: [
1705
+ "chart-surface",
1706
+ "metric-dashboard"
1707
+ ]
1708
+ },
1181
1709
  {
1182
1710
  name: "TCheckbox",
1183
1711
  aliases: [],
@@ -1661,9 +2189,14 @@ var treeui_catalog_default = {
1661
2189
  "The page shell should stay full-width while content stays readable."
1662
2190
  ],
1663
2191
  avoidWhen: [
1664
- "You need internal spacing between children."
2192
+ "You need internal spacing between children.",
2193
+ "You are laying out a whole screen \u2014 reach for TPage, which adds page padding and vertical rhythm."
1665
2194
  ],
1666
2195
  alternatives: [
2196
+ {
2197
+ component: "TPage",
2198
+ when: "You want a full page region with block padding and vertical spacing between sections, not just a width constraint."
2199
+ },
1667
2200
  {
1668
2201
  component: "TStack",
1669
2202
  when: "The main need is vertical or horizontal spacing."
@@ -2149,6 +2682,142 @@ var treeui_catalog_default = {
2149
2682
  },
2150
2683
  recipes: []
2151
2684
  },
2685
+ {
2686
+ name: "TDonutChart",
2687
+ aliases: [],
2688
+ package: "@treeui/vue",
2689
+ kind: "data-display",
2690
+ category: "Data Display",
2691
+ status: "available",
2692
+ purpose: "Part-of-whole chart rendered as a donut (default) or full pie, with a legend, percentages, an interactive center readout, and an accessible label.",
2693
+ sourceFile: "packages/vue/src/components/TDonutChart.vue",
2694
+ storyFile: "apps/docs/src/stories/DonutChart.stories.ts",
2695
+ storyTitle: "Components/Data Display/Donut Chart",
2696
+ manifestFile: "docs/ai/COMPONENTS/donut-chart.yaml",
2697
+ props: [
2698
+ {
2699
+ name: "segments",
2700
+ type: "TDonutSegment[]",
2701
+ default: "[]",
2702
+ note: "Each item is { label: string; value: number; color?: string }. Non-positive values are omitted."
2703
+ },
2704
+ {
2705
+ name: "size",
2706
+ type: "number",
2707
+ default: "200",
2708
+ note: "Square canvas in px."
2709
+ },
2710
+ {
2711
+ name: "thickness",
2712
+ type: "number",
2713
+ default: "26",
2714
+ note: "Ring thickness; 0 renders a full pie."
2715
+ },
2716
+ {
2717
+ name: "gap",
2718
+ type: "number",
2719
+ default: "2",
2720
+ note: "Gap between slices, in degrees."
2721
+ },
2722
+ {
2723
+ name: "rounded",
2724
+ type: "boolean",
2725
+ default: "false",
2726
+ note: "Round the ends of donut segments (widens the gap to make room). Best with a thinner ring."
2727
+ },
2728
+ {
2729
+ name: "showLegend",
2730
+ type: "boolean",
2731
+ default: "true"
2732
+ },
2733
+ {
2734
+ name: "showPercent",
2735
+ type: "boolean",
2736
+ default: "true"
2737
+ },
2738
+ {
2739
+ name: "centerLabel",
2740
+ type: "string",
2741
+ default: "Total",
2742
+ note: "Donut only; ignored for pie."
2743
+ },
2744
+ {
2745
+ name: "centerValue",
2746
+ type: "string | number",
2747
+ default: "auto (sum of values)"
2748
+ },
2749
+ {
2750
+ name: "valueFormat",
2751
+ type: "(value: number) => string",
2752
+ default: "locale integers for |value| >= 1000"
2753
+ },
2754
+ {
2755
+ name: "loading",
2756
+ type: "boolean",
2757
+ default: "false"
2758
+ },
2759
+ {
2760
+ name: "ariaLabel",
2761
+ type: "string",
2762
+ default: "auto-generated summary"
2763
+ }
2764
+ ],
2765
+ events: [],
2766
+ slots: [
2767
+ {
2768
+ name: "center"
2769
+ },
2770
+ {
2771
+ name: "empty"
2772
+ }
2773
+ ],
2774
+ a11y: [
2775
+ "Renders role=img with an aria-label summarizing the chart.",
2776
+ "Legend + percentages give a non-visual reading of each slice."
2777
+ ],
2778
+ behavior: [
2779
+ "colors_auto_assign_from_chart_palette: --tree-color-chart-1..8, cycling; per-segment color overrides",
2780
+ "hovering_a_slice_dims_the_rest_and_updates_center: true",
2781
+ "center_shows_total_or_the_hovered_segment: true"
2782
+ ],
2783
+ selection: {
2784
+ keywords: [
2785
+ "donut",
2786
+ "doughnut",
2787
+ "pie",
2788
+ "proportion",
2789
+ "share",
2790
+ "breakdown",
2791
+ "part of whole",
2792
+ "percentage"
2793
+ ],
2794
+ useWhen: [
2795
+ "You are showing how a total splits into a handful of categories.",
2796
+ "A donut center readout (total or hovered slice) is useful."
2797
+ ],
2798
+ avoidWhen: [
2799
+ "There are many categories or precise comparison matters \u2014 use TChart bars.",
2800
+ "The data is a trend over time \u2014 use TChart or TSparkline."
2801
+ ],
2802
+ alternatives: [
2803
+ {
2804
+ component: "TChart",
2805
+ when: "Precise magnitude comparison across categories is needed (bars)."
2806
+ }
2807
+ ]
2808
+ },
2809
+ setup: {
2810
+ required: [],
2811
+ notes: [
2812
+ "Pass `segments` as `{ label, value, color? }[]`; non-positive values are dropped.",
2813
+ "Set `thickness: 0` for a full pie; keep the default for a donut with a center readout.",
2814
+ "Use the `center` slot (donut only) for a custom center; otherwise it shows the total or the hovered slice."
2815
+ ]
2816
+ },
2817
+ recipes: [
2818
+ "metric-dashboard"
2819
+ ]
2820
+ },
2152
2821
  {
2153
2822
  name: "TDrawer",
2154
2823
  aliases: [],
@@ -2350,6 +3019,16 @@ var treeui_catalog_default = {
2350
3019
  name: "label",
2351
3020
  type: "string",
2352
3021
  default: ""
3022
+ },
3023
+ {
3024
+ name: "align",
3025
+ type: "TDropdownAlign",
3026
+ default: "start",
3027
+ values: [
3028
+ "start",
3029
+ "end"
3030
+ ],
3031
+ note: "Horizontal alignment of the menu relative to the trigger. Use `end` when the trigger sits near the right edge of a clipping container (e.g. a table actions column)."
2353
3032
  }
2354
3033
  ],
2355
3034
  events: [
@@ -2385,7 +3064,8 @@ var treeui_catalog_default = {
2385
3064
  behavior: [
2386
3065
  "outside_click_closes_menu: true",
2387
3066
  "focus_restores_on_close: true",
2388
- "chevron_rotates_when_open: true"
3067
+ "chevron_rotates_when_open: true",
3068
+ "menu_grows_to_fit_items_without_wrapping: true"
2389
3069
  ],
2390
3070
  selection: {
2391
3071
  keywords: [
@@ -2515,7 +3195,7 @@ var treeui_catalog_default = {
2515
3195
  kind: "form",
2516
3196
  category: "Form",
2517
3197
  status: "available",
2518
- purpose: "Composable dropzone and file picker with browse, drag-and-drop, and Ctrl+V paste support.",
3198
+ purpose: "Composable dropzone and file picker with browse, drag-and-drop, and Ctrl+V paste support, plus per-file upload state: progress, remaining time, thumbnails, and independent per-file retry/resume.",
2519
3199
  sourceFile: "packages/vue/src/components/TFileUpload.vue",
2520
3200
  storyFile: "apps/docs/src/stories/FileUpload.stories.ts",
2521
3201
  storyTitle: "Components/Form/FileUpload",
@@ -2539,12 +3219,14 @@ var treeui_catalog_default = {
2539
3219
  {
2540
3220
  name: "disabled",
2541
3221
  type: "boolean",
2542
- default: "false"
3222
+ default: "false",
3223
+ description: "The ONLY prop that gates interaction. Disables the dropzone, the native input, Clear all, and every per-row Remove/Retry."
2543
3224
  },
2544
3225
  {
2545
3226
  name: "loading",
2546
3227
  type: "boolean",
2547
- default: "false"
3228
+ default: "false",
3229
+ description: "Presentational only. Applies `is-loading`, `aria-busy` on the dropzone, and the corner spinner. It gates NO interaction \u2014 see behavior.loading_is_presentational."
2548
3230
  },
2549
3231
  {
2550
3232
  name: "invalid",
@@ -2577,72 +3259,170 @@ var treeui_catalog_default = {
2577
3259
  default: "Drop files here"
2578
3260
  },
2579
3261
  {
2580
- name: "description",
3262
+ name: "description",
3263
+ type: "string",
3264
+ default: "Click to browse, drag and drop, or paste with Ctrl+V."
3265
+ },
3266
+ {
3267
+ name: "buttonLabel",
3268
+ type: "string",
3269
+ default: "Browse files"
3270
+ },
3271
+ {
3272
+ name: "filesLabel",
3273
+ type: "string",
3274
+ default: "Selected files"
3275
+ },
3276
+ {
3277
+ name: "paste",
3278
+ type: "boolean",
3279
+ default: "true"
3280
+ },
3281
+ {
3282
+ name: "drop",
3283
+ type: "boolean",
3284
+ default: "true"
3285
+ },
3286
+ {
3287
+ name: "showFileList",
3288
+ type: "boolean",
3289
+ default: "true"
3290
+ },
3291
+ {
3292
+ name: "uploadState",
3293
+ type: "TFileUploadStateSource",
3294
+ default: "none",
3295
+ description: "Per-file transport state, keyed by File OBJECT IDENTITY. Accepts a ReadonlyMap<File, TFileUploadState> or a (file, index) => TFileUploadState | undefined resolver. Omit it and the component renders exactly as before: no bars, no status lines, no retry."
3296
+ },
3297
+ {
3298
+ name: "dragLabel",
3299
+ type: "string",
3300
+ default: "Release to upload",
3301
+ description: "Replaces `label` in the dropzone while an acceptable drag is over it. Set it to the same value as `label` to opt out of the text swap."
3302
+ },
3303
+ {
3304
+ name: "dragRejectLabel",
2581
3305
  type: "string",
2582
- default: "Click to browse, drag and drop, or paste with Ctrl+V."
3306
+ default: "This file type is not accepted",
3307
+ description: "Replaces `label` while a drag that cannot work is over the dropzone. Only reachable when drag-reject detection is possible \u2014 see behavior.drag_reject_detection_is_mime_only."
2583
3308
  },
2584
3309
  {
2585
- name: "buttonLabel",
2586
- type: "string",
2587
- default: "Browse files"
3310
+ name: "thumbnails",
3311
+ type: "boolean",
3312
+ default: "true",
3313
+ description: "Mint object-URL previews and decode dimensions for image/* files. Set false when you always supply `state.thumbnailUrl` yourself."
2588
3314
  },
2589
3315
  {
2590
- name: "filesLabel",
3316
+ name: "maxThumbnailSize",
3317
+ type: "number | null",
3318
+ default: "10 * 1024 * 1024",
3319
+ description: "Skip auto-thumbnailing above this byte size. null removes the cap."
3320
+ },
3321
+ {
3322
+ name: "retryLabel",
2591
3323
  type: "string",
2592
- default: "Selected files"
3324
+ default: "Retry"
2593
3325
  },
2594
3326
  {
2595
- name: "paste",
2596
- type: "boolean",
2597
- default: "true"
3327
+ name: "resumeLabel",
3328
+ type: "(percent: number) => string",
3329
+ default: "(percent) => `Resume from ${percent}%`",
3330
+ description: "Receives Math.round(state.progress). Used only when state.resumable === true AND state.progress is a number."
2598
3331
  },
2599
3332
  {
2600
- name: "drop",
2601
- type: "boolean",
2602
- default: "true"
3333
+ name: "remainingTimeFormat",
3334
+ type: "(remainingMs: number) => string",
3335
+ default: "5s/60s/60min thresholds \u2014 see behavior.remaining_time_strings",
3336
+ description: "Formats state.remainingMs. Ignored for a row that supplies state.remainingLabel."
2603
3337
  },
2604
3338
  {
2605
- name: "showFileList",
2606
- type: "boolean",
2607
- default: "true"
3339
+ name: "statusLabels",
3340
+ type: "Partial<Record<TFileUploadStatus, string>>",
3341
+ default: "{}",
3342
+ description: "MERGED over the defaults { pending: Waiting, uploading: Uploading, paused: Paused, error: Upload failed, success: Uploaded } \u2014 pass only the keys you override."
2608
3343
  }
2609
3344
  ],
2610
3345
  events: [
2611
3346
  {
2612
- name: "update:modelValue"
3347
+ name: "update:modelValue",
3348
+ description: "Emitted with the next file list when files are added, removed, or cleared.",
3349
+ payload: "File[]"
3350
+ },
3351
+ {
3352
+ name: "files-accepted",
3353
+ description: "Emitted with only the newly accepted files, after accept/maxFiles/maxFileSize validation.",
3354
+ payload: "File[]"
2613
3355
  },
2614
3356
  {
2615
- name: "files-accepted"
3357
+ name: "files-rejected",
3358
+ description: "Pre-acceptance validation failures. Unrelated to transport errors, which you report through `uploadState`.",
3359
+ payload: "TFileUploadRejection[]"
2616
3360
  },
2617
3361
  {
2618
- name: "files-rejected"
3362
+ name: "retry",
3363
+ description: "Emitted when a row's retry affordance is activated. Pure intent \u2014 the component performs no upload. Use `payload.mode` ('resume' | 'restart'); it reports the affordance the component ACTUALLY DREW, so never re-derive it from your own state.",
3364
+ payload: "TFileUploadRetryPayload"
3365
+ },
3366
+ {
3367
+ name: "cancel",
3368
+ description: "Emitted ONLY for rows whose status is 'uploading' or 'paused', immediately BEFORE the `update:modelValue` that drops them \u2014 so the file is still in the list when your handler runs and you can abort the request. `reason` is 'remove' for one row or 'clear' (one event per in-flight file) for Clear all.",
3369
+ payload: "TFileUploadCancelPayload"
2619
3370
  }
2620
3371
  ],
2621
3372
  slots: [
2622
3373
  {
2623
- name: "icon"
3374
+ name: "icon",
3375
+ description: "Replaces the dropzone icon."
2624
3376
  },
2625
3377
  {
2626
- name: "default"
3378
+ name: "default",
3379
+ description: "Replaces the dropzone body. Receives `{ files, isDragActive, isDragReject, openFileDialog, clearFiles }`."
2627
3380
  },
2628
3381
  {
2629
- name: "file"
3382
+ name: "file",
3383
+ description: "Full row override. Receives all of TFileUploadFileSlotProps, including every derived label, so a custom row re-derives nothing."
2630
3384
  },
2631
3385
  {
2632
- name: "actions"
3386
+ name: "file-status",
3387
+ description: "Replaces ONLY the progress bar + status line + error block inside the default row. Receives TFileUploadFileSlotProps. Name, meta, thumbnail, Retry, and Remove keep rendering. Ignored when the `file` slot is provided."
3388
+ },
3389
+ {
3390
+ name: "actions",
3391
+ description: "Footer actions. Receives `{ files, openFileDialog, clearFiles }` \u2014 no upload state."
2633
3392
  }
2634
3393
  ],
2635
3394
  a11y: [
2636
3395
  "dropzone_role: button",
2637
3396
  "keyboard_activation: Enter_and_Space_open_the_file_picker",
2638
3397
  "supports_paste_when_component_is_focused: true",
2639
- "file_feedback_announced_via_live_region: true"
3398
+ "file_feedback_announced_via_live_region: true",
3399
+ "aria_busy_on_dropzone_tracks_loading_prop: true",
3400
+ "aria_busy_on_row_tracks_that_rows_uploading_status: true",
3401
+ "per_row_progressbar_is_labelled_with_status_and_filename: e.g. 'Uploading hero.png' \u2014 every row's bar has a distinct accessible name.",
3402
+ "terminal_transitions_announced_with_filenames: One polite root live region (`role=log`) announces only success/error, e.g. 'alpha.txt uploaded.' / 'alpha.txt failed. Connection lost.'",
3403
+ "repeated_identical_messages_are_still_announced: The log appends each message as a new node rather than overwriting one string, so a retry that fails with the byte-identical error still mutates the region and is announced. It is not aria-atomic: only the appended message is read, and the trimmed history (5 most recent) is not.",
3404
+ "percentages_are_never_announced: Percent and ETA live in non-live text; the value reaches assistive tech through the progressbar's aria-valuenow.",
3405
+ "retry_button_is_described_by_its_row_error: Announces 'Resume from 90%, Connection lost at 90%'.",
3406
+ "focus_is_rescued_when_retry_unmounts: Flipping error -> uploading removes the focused Retry; focus moves to that row's Remove (or the dropzone), and only when it was genuinely lost.",
3407
+ "remove_and_retry_meet_the_44px_target: true"
2640
3408
  ],
2641
3409
  behavior: [
2642
3410
  "accepts_files_from_input_drop_and_paste: true",
2643
3411
  "multiple_false_replaces_existing_selection: true",
2644
3412
  "accept_maxFiles_and_maxFileSize_apply_to_all_entry_points: true",
2645
- "selected_files_can_be_removed_or_cleared: true"
3413
+ "selected_files_can_be_removed_or_cleared: true",
3414
+ "upload_state_is_keyed_by_file_identity: Both source shapes key on the File object, never on index or name. Key your own store with the File objects you receive from `files-accepted`.",
3415
+ "a_lookup_miss_renders_nothing: An unknown File yields `undefined` and the row falls back to name + meta only \u2014 no bar, no status, no retry. This fail-blank is deliberate: it is the back-compat path and it fails loudly instead of showing another file's percentage.",
3416
+ "loading_is_presentational: BREAKING since 0.14.0 \u2014 `loading` no longer disables anything. It was freezing the dropzone, Clear all, and every Remove, which blocked removing file 1 while file 4 uploaded. Use `:disabled` to reproduce the old behavior; prefer `uploadState` and freeze nothing.",
3417
+ "each_file_has_an_independent_track: One row's error, progress, or retry never gates another's. Remove stays enabled for in-flight rows and emits `cancel` so you can abort.",
3418
+ "errors_are_per_row: Transport errors go in `state.error` and render under their own row. They never enter the dropzone feedback list, which Remove and Clear all wipe.",
3419
+ "drag_feedback_has_three_signals: Border, glow, and a text swap to `dragLabel` \u2014 all three fire together, including when files are already listed.",
3420
+ "drag_reject_detection_is_mime_only: `is-drag-reject` + `dragRejectLabel` require every `accept` token to be a MIME token; with any `.ext` token the dropzone stays neutral, because a drag exposes only `.type`. A mixed drag (one good file, one bad) is NOT rejected, and the drop is never blocked \u2014 rejected files always get the normal `files-rejected` explanation.",
3421
+ "progress_bar_visibility: Shown on uploading/paused/error, and on success only with numeric progress. 'uploading' without progress is indeterminate; paused/error without progress is a determinate 0 \u2014 an errored bar must never animate.",
3422
+ "the_bar_is_never_reset_on_error: It holds `state.progress` in the error tone. That retained 90% is what makes the resume promise legible.",
3423
+ "remaining_time_strings: < 5000ms -> 'Less than 5s left'; < 60s -> 'About 12s left'; < 60min -> 'About 2 min left'; else 'About 2h left'. Negative, NaN, and Infinity render nothing.",
3424
+ "status_line_composition: Joins the present parts with ' \xB7 ': 'Uploading \xB7 62% \xB7 About 12s left' while uploading, 'Upload failed \xB7 2.1 MB of 2.4 MB uploaded' on a resumable error.",
3425
+ "thumbnails_are_client_only: Object URLs are minted after mount and revoked on removal and unmount. SSR and the hydrating render both show the extension chip, so there is no mismatch."
2646
3426
  ],
2647
3427
  selection: {
2648
3428
  keywords: [],
@@ -2885,7 +3665,8 @@ var treeui_catalog_default = {
2885
3665
  },
2886
3666
  recipes: [
2887
3667
  "date-range-form",
2888
- "stat-grid"
3668
+ "stat-grid",
3669
+ "metric-dashboard"
2889
3670
  ]
2890
3671
  },
2891
3672
  {
@@ -3696,7 +4477,7 @@ var treeui_catalog_default = {
3696
4477
  name: "items",
3697
4478
  type: "TNavMenuItem[]",
3698
4479
  default: "[]",
3699
- note: "Array of `{ label, value, shortLabel?, description?, icon?, to?, badge?, disabled? }`."
4480
+ note: "Array of `{ label, value, shortLabel?, description?, icon?, to?, badge?, disabled?, exact? }`."
3700
4481
  },
3701
4482
  {
3702
4483
  name: "size",
@@ -3717,6 +4498,12 @@ var treeui_catalog_default = {
3717
4498
  name: "disabled",
3718
4499
  type: "boolean",
3719
4500
  default: "false"
4501
+ },
4502
+ {
4503
+ name: "exact",
4504
+ type: "boolean",
4505
+ default: "true",
4506
+ note: "Default active-match strategy for router-link items. `true` keeps only the exactly-active route highlighted (prevents prefix matches from lighting up several items at once). Override per item via `TNavMenuItem.exact`."
3720
4507
  }
3721
4508
  ],
3722
4509
  events: [
@@ -3775,10 +4562,11 @@ var treeui_catalog_default = {
3775
4562
  setup: {
3776
4563
  required: [],
3777
4564
  notes: [
3778
- "When nested inside `TSidebar`, `collapsed` can be inherited from the sidebar provider."
4565
+ "When nested inside `TSidebar` or `TAppShell`, `collapsed` can be inherited from the sidebar provider."
3779
4566
  ]
3780
4567
  },
3781
4568
  recipes: [
4569
+ "app-shell",
3782
4570
  "sidebar-shell"
3783
4571
  ]
3784
4572
  },
@@ -3889,6 +4677,193 @@ var treeui_catalog_default = {
3889
4677
  },
3890
4678
  recipes: []
3891
4679
  },
4680
+ {
4681
+ name: "TPage",
4682
+ aliases: [],
4683
+ package: "@treeui/vue",
4684
+ kind: "layout",
4685
+ category: "Layout",
4686
+ status: "available",
4687
+ purpose: "Semantic page region under the app shell: block padding, a centered max-width content column, and standardized vertical rhythm between sections.",
4688
+ sourceFile: "packages/vue/src/components/TPage.vue",
4689
+ storyFile: "apps/docs/src/stories/Page.stories.ts",
4690
+ storyTitle: "Components/Layout/Page",
4691
+ manifestFile: "docs/ai/COMPONENTS/page.yaml",
4692
+ props: [
4693
+ {
4694
+ name: "as",
4695
+ type: "string",
4696
+ default: "div"
4697
+ },
4698
+ {
4699
+ name: "width",
4700
+ type: "TContainerSize",
4701
+ default: "lg",
4702
+ values: [
4703
+ "sm",
4704
+ "md",
4705
+ "lg",
4706
+ "xl",
4707
+ "full"
4708
+ ],
4709
+ note: "Max-width of the centered content column. Mirrors TContainer sizes."
4710
+ },
4711
+ {
4712
+ name: "padded",
4713
+ type: "boolean",
4714
+ default: "true",
4715
+ note: "Adds vertical (block) padding on the region and horizontal padding on the inner column."
4716
+ },
4717
+ {
4718
+ name: "gap",
4719
+ type: "TPageGap",
4720
+ default: "lg",
4721
+ values: [
4722
+ "sm",
4723
+ "md",
4724
+ "lg"
4725
+ ],
4726
+ note: "Vertical spacing between direct children (e.g. a TPageHeader and content sections)."
4727
+ }
4728
+ ],
4729
+ events: [],
4730
+ slots: [
4731
+ {
4732
+ name: "default"
4733
+ }
4734
+ ],
4735
+ a11y: [
4736
+ "Structural wrapper only; landmark semantics come from `as` and the surrounding app shell (avoid nesting a second `main`)."
4737
+ ],
4738
+ behavior: [
4739
+ "centers_content_column_by_width: true",
4740
+ "applies_block_padding_when_padded: true",
4741
+ "spaces_children_vertically_by_gap: true"
4742
+ ],
4743
+ selection: {
4744
+ keywords: [
4745
+ "page",
4746
+ "page shell",
4747
+ "content column",
4748
+ "screen layout"
4749
+ ],
4750
+ useWhen: [
4751
+ "You want a standard screen region under the app shell with consistent max width, page padding, and vertical rhythm between sections."
4752
+ ],
4753
+ avoidWhen: [
4754
+ "You only need a bare width constraint with no page padding or child spacing."
4755
+ ],
4756
+ alternatives: [
4757
+ {
4758
+ component: "TContainer",
4759
+ when: "You just need a centered max-width wrapper without page-level block padding or child spacing."
4760
+ },
4761
+ {
4762
+ component: "TStack",
4763
+ when: "You only need to space a group of children, not lay out a whole page."
4764
+ }
4765
+ ]
4766
+ },
4767
+ setup: {
4768
+ required: [],
4769
+ notes: []
4770
+ },
4771
+ recipes: []
4772
+ },
4773
+ {
4774
+ name: "TPageHeader",
4775
+ aliases: [],
4776
+ package: "@treeui/vue",
4777
+ kind: "layout",
4778
+ category: "Layout",
4779
+ status: "available",
4780
+ purpose: "Canonical page title block: heading, optional subtitle, and a right-aligned actions area, so title hierarchy and alignment stay consistent across screens.",
4781
+ sourceFile: "packages/vue/src/components/TPageHeader.vue",
4782
+ storyFile: "apps/docs/src/stories/PageHeader.stories.ts",
4783
+ storyTitle: "Components/Layout/PageHeader",
4784
+ manifestFile: "docs/ai/COMPONENTS/page-header.yaml",
4785
+ props: [
4786
+ {
4787
+ name: "title",
4788
+ type: "string",
4789
+ default: "",
4790
+ note: "Rendered inside the heading element. Overridden by the `title` slot."
4791
+ },
4792
+ {
4793
+ name: "subtitle",
4794
+ type: "string",
4795
+ default: ""
4796
+ },
4797
+ {
4798
+ name: "level",
4799
+ type: "1 | 2 | 3 | 4 | 5 | 6",
4800
+ default: "1",
4801
+ note: "Heading level for the title element. Default `1` treats it as the page title; lower it when the page already has an h1."
4802
+ },
4803
+ {
4804
+ name: "as",
4805
+ type: "string",
4806
+ default: "header"
4807
+ }
4808
+ ],
4809
+ events: [],
4810
+ slots: [
4811
+ {
4812
+ name: "breadcrumb"
4813
+ },
4814
+ {
4815
+ name: "title"
4816
+ },
4817
+ {
4818
+ name: "subtitle"
4819
+ },
4820
+ {
4821
+ name: "actions"
4822
+ },
4823
+ {
4824
+ name: "default"
4825
+ }
4826
+ ],
4827
+ a11y: [
4828
+ "Title renders as a real heading element (`h1`\u2013`h6`) chosen by `level`; keep one h1 per page.",
4829
+ "Actions area holds standard focusable controls (e.g. TButton) \u2014 no custom roles added."
4830
+ ],
4831
+ behavior: [
4832
+ "title_and_subtitle_optional: true",
4833
+ "heading_level_controlled_by_level: true",
4834
+ "actions_area_right_aligned_and_wraps: true",
4835
+ "default_slot_renders_below_heading: true"
4836
+ ],
4837
+ selection: {
4838
+ keywords: [
4839
+ "page header",
4840
+ "title bar",
4841
+ "page title",
4842
+ "heading and actions"
4843
+ ],
4844
+ useWhen: [
4845
+ "A screen needs a title (and optional subtitle) with a right-aligned actions area in a consistent hierarchy."
4846
+ ],
4847
+ avoidWhen: [
4848
+ "You need cross-page hierarchy or navigation trails rather than a single page title."
4849
+ ],
4850
+ alternatives: [
4851
+ {
4852
+ component: "TBreadcrumb",
4853
+ when: "You are showing where the page sits in a hierarchy, not its title."
4854
+ },
4855
+ {
4856
+ component: "TNavbar",
4857
+ when: "You need a top application bar, not a per-page title block."
4858
+ }
4859
+ ]
4860
+ },
4861
+ setup: {
4862
+ required: [],
4863
+ notes: []
4864
+ },
4865
+ recipes: []
4866
+ },
3892
4867
  {
3893
4868
  name: "TPagination",
3894
4869
  aliases: [],
@@ -4605,7 +5580,8 @@ var treeui_catalog_default = {
4605
5580
  "check_icon_on_selected_option: true",
4606
5581
  "outside_click_closes_dropdown: true",
4607
5582
  "focus_restores_on_close: true",
4608
- "numeric_values_supported_without_string_coercion: true"
5583
+ "numeric_values_supported_without_string_coercion: true",
5584
+ "listbox_flips_above_trigger_when_space_below_is_insufficient: true"
4609
5585
  ],
4610
5586
  selection: {
4611
5587
  keywords: [
@@ -4638,7 +5614,9 @@ var treeui_catalog_default = {
4638
5614
  "Use `placeholder` when no selection has been made."
4639
5615
  ]
4640
5616
  },
4641
- recipes: []
5617
+ recipes: [
5618
+ "chart-surface"
5619
+ ]
4642
5620
  },
4643
5621
  {
4644
5622
  name: "TSelectableList",
@@ -4860,7 +5838,8 @@ var treeui_catalog_default = {
4860
5838
  "controlled_and_uncontrolled_collapsed_state: true",
4861
5839
  "provides_collapsed_state_to_descendants: true",
4862
5840
  "left_and_right_variants: true",
4863
- "exposes_slot_props_with_collapsed_state: true"
5841
+ "exposes_slot_props_with_collapsed_state: true",
5842
+ "collapsed_state_centers_header_and_footer_content: true"
4864
5843
  ],
4865
5844
  selection: {
4866
5845
  keywords: [
@@ -4885,7 +5864,8 @@ var treeui_catalog_default = {
4885
5864
  required: [],
4886
5865
  notes: [
4887
5866
  "Pair with `TNavMenu` for app-section navigation.",
4888
- "Use controlled `collapsed` state when the app shell persists layout preferences."
5867
+ "Use controlled `collapsed` state when the app shell persists layout preferences.",
5868
+ "Prefer `TAppShell` when you also want full-viewport sizing, a top header, and a responsive mobile drawer."
4889
5869
  ]
4890
5870
  },
4891
5871
  recipes: [
@@ -4955,6 +5935,129 @@ var treeui_catalog_default = {
4955
5935
  },
4956
5936
  recipes: []
4957
5937
  },
5938
+ {
5939
+ name: "TSparkline",
5940
+ aliases: [],
5941
+ package: "@treeui/vue",
5942
+ kind: "data-display",
5943
+ category: "Data Display",
5944
+ status: "available",
5945
+ purpose: "Tiny, axis-less inline trend chart (line, area, or bar) sized for stat tiles, table cells, and running text.",
5946
+ sourceFile: "packages/vue/src/components/TSparkline.vue",
5947
+ storyFile: "apps/docs/src/stories/Sparkline.stories.ts",
5948
+ storyTitle: "Components/Data Display/Sparkline",
5949
+ manifestFile: "docs/ai/COMPONENTS/sparkline.yaml",
5950
+ props: [
5951
+ {
5952
+ name: "data",
5953
+ type: "number[]",
5954
+ default: "[]"
5955
+ },
5956
+ {
5957
+ name: "type",
5958
+ type: "line | area | bar",
5959
+ default: "line"
5960
+ },
5961
+ {
5962
+ name: "color",
5963
+ type: "string",
5964
+ default: "var(--tree-color-chart-1)",
5965
+ note: "Any CSS color."
5966
+ },
5967
+ {
5968
+ name: "width",
5969
+ type: "number",
5970
+ default: "120"
5971
+ },
5972
+ {
5973
+ name: "height",
5974
+ type: "number",
5975
+ default: "32"
5976
+ },
5977
+ {
5978
+ name: "smooth",
5979
+ type: "boolean",
5980
+ default: "false",
5981
+ note: "Curved line/area."
5982
+ },
5983
+ {
5984
+ name: "strokeWidth",
5985
+ type: "number",
5986
+ default: "2",
5987
+ note: "Line/area only."
5988
+ },
5989
+ {
5990
+ name: "min",
5991
+ type: "number",
5992
+ default: "data min"
5993
+ },
5994
+ {
5995
+ name: "max",
5996
+ type: "number",
5997
+ default: "data max"
5998
+ },
5999
+ {
6000
+ name: "showLastPoint",
6001
+ type: "boolean",
6002
+ default: "false",
6003
+ note: "Marker on the final point (line/area)."
6004
+ },
6005
+ {
6006
+ name: "ariaLabel",
6007
+ type: "string",
6008
+ default: "Trend sparkline"
6009
+ }
6010
+ ],
6011
+ events: [],
6012
+ slots: [],
6013
+ a11y: [
6014
+ "Renders role=img with an aria-label; pass a specific one for meaningful trends.",
6015
+ "Pair with a visible number \u2014 a sparkline supplements a value, it does not replace it."
6016
+ ],
6017
+ behavior: [
6018
+ "no_axes_or_legend: true",
6019
+ "flat_series_renders_centered: true",
6020
+ "scales_to_intrinsic_width_and_height: true"
6021
+ ],
6022
+ selection: {
6023
+ keywords: [
6024
+ "sparkline",
6025
+ "mini chart",
6026
+ "inline chart",
6027
+ "micro trend",
6028
+ "trend line",
6029
+ "table chart"
6030
+ ],
6031
+ useWhen: [
6032
+ "You want a compact trend beside a value, in a table cell, or inline in text.",
6033
+ "No axes, legend, or tooltip are required."
6034
+ ],
6035
+ avoidWhen: [
6036
+ "You need axes, multiple labeled series, or a tooltip \u2014 use TChart."
6037
+ ],
6038
+ alternatives: [
6039
+ {
6040
+ component: "TChart",
6041
+ when: "The chart needs axes, a legend, or a tooltip."
6042
+ },
6043
+ {
6044
+ component: "TStat",
6045
+ when: "A single value with a trend badge is enough."
6046
+ }
6047
+ ]
6048
+ },
6049
+ setup: {
6050
+ required: [],
6051
+ notes: [
6052
+ "Pass `data` as `number[]`; there are no axes, legend, or tooltip by design.",
6053
+ "Size it with `width`/`height`; use it inside `TStat`, table cells, or text.",
6054
+ "Always keep a visible number nearby \u2014 a sparkline supplements a value, it does not replace it."
6055
+ ]
6056
+ },
6057
+ recipes: [
6058
+ "metric-dashboard"
6059
+ ]
6060
+ },
4958
6061
  {
4959
6062
  name: "TSpinner",
4960
6063
  aliases: [],
@@ -5208,6 +6311,10 @@ var treeui_catalog_default = {
5208
6311
  {
5209
6312
  component: "TCard",
5210
6313
  when: "The content is not a metric."
6314
+ },
6315
+ {
6316
+ component: "TSparkline",
6317
+ when: "You want to show the metric's trend as an inline mini-chart."
5211
6318
  }
5212
6319
  ]
5213
6320
  },
@@ -5216,7 +6323,8 @@ var treeui_catalog_default = {
5216
6323
  notes: []
5217
6324
  },
5218
6325
  recipes: [
5219
- "stat-grid"
6326
+ "stat-grid",
6327
+ "metric-dashboard"
5220
6328
  ]
5221
6329
  },
5222
6330
  {