@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/cli.cjs CHANGED
@@ -34,9 +34,9 @@ var import_server = require("@modelcontextprotocol/sdk/server/index.js");
34
34
 
35
35
  // src/generated/treeui.catalog.json
36
36
  var treeui_catalog_default = {
37
- generatedAt: "2026-06-26T22:57:20.487Z",
37
+ generatedAt: "2026-07-18T01:52:38.578Z",
38
38
  packageName: "@treeui/vue",
39
- packageVersion: "0.8.0",
39
+ packageVersion: "0.14.0",
40
40
  decisionGuides: [
41
41
  {
42
42
  id: "single-choice",
@@ -234,6 +234,50 @@ var treeui_catalog_default = {
234
234
  notes: []
235
235
  }
236
236
  ]
237
+ },
238
+ {
239
+ id: "data-viz-choice",
240
+ question: "Visualize numeric data",
241
+ options: [
242
+ {
243
+ use: "TStat",
244
+ when: [
245
+ "The story is a single headline number, optionally with a trend and delta.",
246
+ "It is not a plot \u2014 prefer the number over a chart when one value says it."
247
+ ],
248
+ notes: []
249
+ },
250
+ {
251
+ use: "TSparkline",
252
+ when: [
253
+ "You want an inline micro-trend next to a value, in a table cell, or in text.",
254
+ "No axes, legend, or tooltip are needed."
255
+ ],
256
+ notes: []
257
+ },
258
+ {
259
+ use: "TChart",
260
+ when: [
261
+ "You need line, area, or bar plots with axes, gridlines, legend, and hover tooltip.",
262
+ "There are one or more series over shared categories."
263
+ ],
264
+ notes: []
265
+ },
266
+ {
267
+ use: "TDonutChart",
268
+ when: [
269
+ "The data is a part-of-whole breakdown (donut or pie)."
270
+ ],
271
+ notes: []
272
+ },
273
+ {
274
+ use: "TProgress",
275
+ when: [
276
+ "You are showing completion toward a single target, not a distribution or trend."
277
+ ],
278
+ notes: []
279
+ }
280
+ ]
237
281
  }
238
282
  ],
239
283
  recipes: [
@@ -283,11 +327,28 @@ var treeui_catalog_default = {
283
327
  "Set end `min` from the selected start time when appropriate."
284
328
  ]
285
329
  },
330
+ {
331
+ id: "app-shell",
332
+ name: "Full-viewport application shell",
333
+ useWhen: [
334
+ "The app needs a header plus side navigation that fills the whole screen and adapts to mobile.",
335
+ "The layout must use the full page width on large monitors, not a centered column."
336
+ ],
337
+ compose: [
338
+ "TAppShell",
339
+ "TNavMenu"
340
+ ],
341
+ notes: [
342
+ "`TAppShell` orchestrates the header, sidebar, and fluid content in one component; put a `TNavMenu` in its `sidebar` slot and it inherits collapsed state automatically.",
343
+ "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.",
344
+ "Mount it at the app root. Wrap only inner regions in `TContainer` if a specific area needs a constrained reading width."
345
+ ]
346
+ },
286
347
  {
287
348
  id: "sidebar-shell",
288
349
  name: "Sidebar application shell",
289
350
  useWhen: [
290
- "The screen needs persistent section navigation."
351
+ "The screen needs persistent section navigation and you are composing the shell by hand."
291
352
  ],
292
353
  compose: [
293
354
  "TSidebar",
@@ -296,7 +357,8 @@ var treeui_catalog_default = {
296
357
  "TContainer"
297
358
  ],
298
359
  notes: [
299
- "Let `TNavMenu` inherit collapsed state from `TSidebar` when possible."
360
+ "Let `TNavMenu` inherit collapsed state from `TSidebar` when possible.",
361
+ "Prefer `TAppShell` (recipe `app-shell`) when you also need full-viewport sizing and a responsive mobile drawer."
300
362
  ]
301
363
  },
302
364
  {
@@ -394,15 +456,38 @@ var treeui_catalog_default = {
394
456
  id: "chart-surface",
395
457
  name: "Chart surface",
396
458
  useWhen: [
397
- "A screen needs data visualization, but the charting engine should stay external to TreeUI."
459
+ "A screen needs data visualization inside a framed, titled surface."
398
460
  ],
399
461
  compose: [
400
462
  "TCard",
401
463
  "TStack",
402
- "external chart library"
464
+ "TChart",
465
+ "TSelect"
403
466
  ],
404
467
  notes: [
405
- "Use TreeUI for the surrounding surface, headings, badges, filters, and empty/loading states."
468
+ "Prefer the native TChart / TDonutChart / TSparkline \u2014 no external charting library is required.",
469
+ "Use TreeUI for the surrounding surface, headings, badges, range filters, and empty/loading states.",
470
+ "TChart has its own loading and empty slots; you can still wrap it in TCard for the frame.",
471
+ "Only reach for an external chart engine for chart types TreeUI does not cover yet (e.g. scatter, heatmap)."
472
+ ]
473
+ },
474
+ {
475
+ id: "metric-dashboard",
476
+ name: "Metric dashboard",
477
+ useWhen: [
478
+ "A dashboard shows KPIs alongside trends and breakdowns."
479
+ ],
480
+ compose: [
481
+ "TStat",
482
+ "TSparkline",
483
+ "TChart",
484
+ "TDonutChart",
485
+ "TGrid"
486
+ ],
487
+ notes: [
488
+ "Lead with TStat tiles; embed a TSparkline in a stat's meta slot for the inline trend.",
489
+ "Use one TChart for the main time series and a TDonutChart for a part-of-whole split.",
490
+ "Keep series colors stable across the dashboard \u2014 rely on the --tree-color-chart-* order, do not remap by rank."
406
491
  ]
407
492
  },
408
493
  {
@@ -552,17 +637,27 @@ var treeui_catalog_default = {
552
637
  "Use `accept`, `multiple`, and size limits in the app layer to constrain uploads."
553
638
  ]
554
639
  },
640
+ TAppShell: {
641
+ required: [],
642
+ notes: [
643
+ "Mount at the app root; it fills the viewport (100dvh, full width) and scrolls its content region independently.",
644
+ "Put navigation in the `sidebar` slot (e.g. `TNavMenu`) \u2014 it inherits collapsed state from the shell provider like `TSidebar`.",
645
+ "Leave `mobile` unset to auto-switch to an off-canvas drawer at `breakpoint`, or bind `mobile` to control it explicitly.",
646
+ "Use `v-model:collapsed` for the desktop rail and `v-model:sidebarOpen` for the mobile drawer when the app persists layout state."
647
+ ]
648
+ },
555
649
  TSidebar: {
556
650
  required: [],
557
651
  notes: [
558
652
  "Pair with `TNavMenu` for app-section navigation.",
559
- "Use controlled `collapsed` state when the app shell persists layout preferences."
653
+ "Use controlled `collapsed` state when the app shell persists layout preferences.",
654
+ "Prefer `TAppShell` when you also want full-viewport sizing, a top header, and a responsive mobile drawer."
560
655
  ]
561
656
  },
562
657
  TNavMenu: {
563
658
  required: [],
564
659
  notes: [
565
- "When nested inside `TSidebar`, `collapsed` can be inherited from the sidebar provider."
660
+ "When nested inside `TSidebar` or `TAppShell`, `collapsed` can be inherited from the sidebar provider."
566
661
  ]
567
662
  },
568
663
  TTreeView: {
@@ -596,6 +691,32 @@ var treeui_catalog_default = {
596
691
  "Use `href` for native links.",
597
692
  "Use `to` when the app has vue-router and breadcrumb items should render RouterLink."
598
693
  ]
694
+ },
695
+ TChart: {
696
+ required: [],
697
+ notes: [
698
+ "Pass `series` as `{ name, data, color? }[]` and `labels` as a parallel `string[]` (one label per data index).",
699
+ "Set `type` to `line`, `area`, or `bar`; use `stacked` only with `bar`.",
700
+ "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.",
701
+ "Pass `valueFormat: (n) => string` to format axis and tooltip values (currency, compact, etc.).",
702
+ "No charting library or provider is required \u2014 the SVG and geometry are built in."
703
+ ]
704
+ },
705
+ TSparkline: {
706
+ required: [],
707
+ notes: [
708
+ "Pass `data` as `number[]`; there are no axes, legend, or tooltip by design.",
709
+ "Size it with `width`/`height`; use it inside `TStat`, table cells, or text.",
710
+ "Always keep a visible number nearby \u2014 a sparkline supplements a value, it does not replace it."
711
+ ]
712
+ },
713
+ TDonutChart: {
714
+ required: [],
715
+ notes: [
716
+ "Pass `segments` as `{ label, value, color? }[]`; non-positive values are dropped.",
717
+ "Set `thickness: 0` for a full pie; keep the default for a donut with a center readout.",
718
+ "Use the `center` slot (donut only) for a custom center; otherwise it shows the total or the hovered slice."
719
+ ]
599
720
  }
600
721
  },
601
722
  appPatterns: {
@@ -609,6 +730,9 @@ var treeui_catalog_default = {
609
730
  commonCompositions: [
610
731
  "TFormField + input-like control",
611
732
  "TGrid + TStat",
733
+ "TStat + TSparkline",
734
+ "TCard + TChart",
735
+ "TAppShell + TNavMenu",
612
736
  "TSidebar + TNavMenu",
613
737
  "TTable + TPagination",
614
738
  "TPricing + TPricingCard"
@@ -769,6 +893,211 @@ var treeui_catalog_default = {
769
893
  },
770
894
  recipes: []
771
895
  },
896
+ {
897
+ name: "TAppShell",
898
+ aliases: [],
899
+ package: "@treeui/vue",
900
+ kind: "layout",
901
+ category: "Layout",
902
+ status: "available",
903
+ 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.",
904
+ sourceFile: "packages/vue/src/components/TAppShell.vue",
905
+ storyFile: "apps/docs/src/stories/AppShell.stories.ts",
906
+ storyTitle: "Components/Layout/AppShell",
907
+ manifestFile: "docs/ai/COMPONENTS/app-shell.yaml",
908
+ props: [
909
+ {
910
+ name: "as",
911
+ type: "string",
912
+ default: "div"
913
+ },
914
+ {
915
+ name: "side",
916
+ type: "TSidebarSide",
917
+ default: "left",
918
+ values: [
919
+ "left",
920
+ "right"
921
+ ]
922
+ },
923
+ {
924
+ name: "sidebarWidth",
925
+ type: "string",
926
+ default: "17.5rem"
927
+ },
928
+ {
929
+ name: "collapsedWidth",
930
+ type: "string",
931
+ default: "4.75rem"
932
+ },
933
+ {
934
+ name: "collapsible",
935
+ type: "boolean",
936
+ default: "false"
937
+ },
938
+ {
939
+ name: "collapsed",
940
+ type: "boolean",
941
+ default: "undefined"
942
+ },
943
+ {
944
+ name: "defaultCollapsed",
945
+ type: "boolean",
946
+ default: "false"
947
+ },
948
+ {
949
+ name: "mobile",
950
+ type: "boolean",
951
+ default: "undefined"
952
+ },
953
+ {
954
+ name: "breakpoint",
955
+ type: "string",
956
+ default: "768px"
957
+ },
958
+ {
959
+ name: "sidebarOpen",
960
+ type: "boolean",
961
+ default: "undefined"
962
+ },
963
+ {
964
+ name: "defaultSidebarOpen",
965
+ type: "boolean",
966
+ default: "false"
967
+ },
968
+ {
969
+ name: "closeOnEscape",
970
+ type: "boolean",
971
+ default: "true"
972
+ },
973
+ {
974
+ name: "closeOnOverlay",
975
+ type: "boolean",
976
+ default: "true"
977
+ },
978
+ {
979
+ name: "showMenuButton",
980
+ type: "boolean",
981
+ default: "true"
982
+ },
983
+ {
984
+ name: "menuLabel",
985
+ type: "string",
986
+ default: "Open menu"
987
+ },
988
+ {
989
+ name: "closeLabel",
990
+ type: "string",
991
+ default: "Close menu"
992
+ },
993
+ {
994
+ name: "sidebarLabel",
995
+ type: "string",
996
+ default: "Sidebar"
997
+ }
998
+ ],
999
+ events: [
1000
+ {
1001
+ name: "update:collapsed"
1002
+ },
1003
+ {
1004
+ name: "collapse-change"
1005
+ },
1006
+ {
1007
+ name: "update:sidebarOpen"
1008
+ },
1009
+ {
1010
+ name: "sidebar-open-change"
1011
+ }
1012
+ ],
1013
+ slots: [
1014
+ {
1015
+ name: "header"
1016
+ },
1017
+ {
1018
+ name: "sidebar"
1019
+ },
1020
+ {
1021
+ name: "default"
1022
+ },
1023
+ {
1024
+ name: "menu-icon"
1025
+ }
1026
+ ],
1027
+ a11y: [
1028
+ "role: dialog",
1029
+ "aria_modal: true",
1030
+ "focus_trap: true",
1031
+ "restores_focus_on_close: true",
1032
+ "escape_closes_when_enabled: true",
1033
+ "menu_button_aria_expanded: true",
1034
+ "menu_button_aria_controls_drawer: true",
1035
+ "sidebar_labelled_by_sidebar_label: true"
1036
+ ],
1037
+ behavior: [
1038
+ "fills_viewport_full_width_and_height: true",
1039
+ "header_spans_full_width_above_sidebar_and_content: true",
1040
+ "content_area_scrolls_independently: true",
1041
+ "auto_switches_to_mobile_at_breakpoint_when_mobile_undefined: true",
1042
+ "mobile_prop_forces_mobile_mode: true",
1043
+ "mobile_sidebar_becomes_off_canvas_drawer: true",
1044
+ "auto_menu_button_in_header_on_mobile: true",
1045
+ "controlled_and_uncontrolled_collapsed_state: true",
1046
+ "controlled_and_uncontrolled_sidebar_open_state: true",
1047
+ "provides_collapsed_state_to_descendants: true",
1048
+ "drawer_reports_collapsed_false_to_descendants_and_slots: true",
1049
+ "left_and_right_variants: true",
1050
+ "locks_body_scroll_while_drawer_open: true",
1051
+ "overlay_click_closes_when_enabled: true",
1052
+ "exposes_slot_props_with_mobile_collapsed_and_toggles: true"
1053
+ ],
1054
+ selection: {
1055
+ keywords: [
1056
+ "app shell",
1057
+ "layout",
1058
+ "dashboard",
1059
+ "admin",
1060
+ "header sidebar",
1061
+ "full screen",
1062
+ "responsive shell",
1063
+ "off-canvas"
1064
+ ],
1065
+ useWhen: [
1066
+ "You need a full-viewport application layout with a header, a side navigation, and a fluid content area.",
1067
+ "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."
1068
+ ],
1069
+ avoidWhen: [
1070
+ "You only need one region (just a top bar, or just a side rail) \u2014 compose TNavbar or TSidebar directly.",
1071
+ "The page content should be centered with a readable max width \u2014 use TContainer inside the shell content instead."
1072
+ ],
1073
+ alternatives: [
1074
+ {
1075
+ component: "TSidebar",
1076
+ when: "You want a standalone collapsible side rail without the header/content orchestration or responsive drawer."
1077
+ },
1078
+ {
1079
+ component: "TNavbar",
1080
+ when: "You only need the top application bar."
1081
+ },
1082
+ {
1083
+ component: "TDrawer",
1084
+ when: "You need a general temporary overlay panel rather than the shell's responsive navigation."
1085
+ }
1086
+ ]
1087
+ },
1088
+ setup: {
1089
+ required: [],
1090
+ notes: [
1091
+ "Mount at the app root; it fills the viewport (100dvh, full width) and scrolls its content region independently.",
1092
+ "Put navigation in the `sidebar` slot (e.g. `TNavMenu`) \u2014 it inherits collapsed state from the shell provider like `TSidebar`.",
1093
+ "Leave `mobile` unset to auto-switch to an off-canvas drawer at `breakpoint`, or bind `mobile` to control it explicitly.",
1094
+ "Use `v-model:collapsed` for the desktop rail and `v-model:sidebarOpen` for the mobile drawer when the app persists layout state."
1095
+ ]
1096
+ },
1097
+ recipes: [
1098
+ "app-shell"
1099
+ ]
1100
+ },
772
1101
  {
773
1102
  name: "TAvatar",
774
1103
  aliases: [],
@@ -992,15 +1321,17 @@ var treeui_catalog_default = {
992
1321
  },
993
1322
  {
994
1323
  name: "variant",
995
- type: "TVariant",
1324
+ type: "TVariant | brand",
996
1325
  default: "solid",
997
1326
  values: [
998
1327
  "solid",
999
1328
  "outline",
1000
1329
  "ghost",
1001
1330
  "soft",
1002
- "danger"
1003
- ]
1331
+ "danger",
1332
+ "brand"
1333
+ ],
1334
+ 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."
1004
1335
  },
1005
1336
  {
1006
1337
  name: "size",
@@ -1130,7 +1461,8 @@ var treeui_catalog_default = {
1130
1461
  ],
1131
1462
  a11y: [],
1132
1463
  behavior: [
1133
- "body_wrapper_class: tree-card__body"
1464
+ "body_wrapper_class: t-card__body",
1465
+ "solid_variant_rescopes_color_tokens_for_nested_content: true"
1134
1466
  ],
1135
1467
  selection: {
1136
1468
  keywords: [
@@ -1165,6 +1497,202 @@ var treeui_catalog_default = {
1165
1497
  "chart-surface"
1166
1498
  ]
1167
1499
  },
1500
+ {
1501
+ name: "TChart",
1502
+ aliases: [],
1503
+ package: "@treeui/vue",
1504
+ kind: "data-display",
1505
+ category: "Data Display",
1506
+ status: "available",
1507
+ 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.",
1508
+ sourceFile: "packages/vue/src/components/TChart.vue",
1509
+ storyFile: "apps/docs/src/stories/Chart.stories.ts",
1510
+ storyTitle: "Components/Data Display/Chart",
1511
+ manifestFile: "docs/ai/COMPONENTS/chart.yaml",
1512
+ props: [
1513
+ {
1514
+ name: "type",
1515
+ type: "TChartType",
1516
+ default: "line",
1517
+ values: [
1518
+ "line",
1519
+ "area",
1520
+ "bar"
1521
+ ]
1522
+ },
1523
+ {
1524
+ name: "series",
1525
+ type: "TChartSeries[]",
1526
+ default: "[]",
1527
+ note: "Each item is { name: string; data: number[]; color?: string }."
1528
+ },
1529
+ {
1530
+ name: "labels",
1531
+ type: "string[]",
1532
+ default: "[]",
1533
+ note: "X-axis categories, aligned index-for-index to each series' data."
1534
+ },
1535
+ {
1536
+ name: "height",
1537
+ type: "number",
1538
+ default: "260",
1539
+ note: "Width is fluid (measured via ResizeObserver)."
1540
+ },
1541
+ {
1542
+ name: "smooth",
1543
+ type: "boolean",
1544
+ default: "false",
1545
+ note: "Curved line/area."
1546
+ },
1547
+ {
1548
+ name: "stacked",
1549
+ type: "boolean",
1550
+ default: "false",
1551
+ note: "Stacks bar series; ignored for line/area. Negative values are clamped to 0 in stacked mode (grouped mode renders them)."
1552
+ },
1553
+ {
1554
+ name: "showGrid",
1555
+ type: "boolean",
1556
+ default: "true"
1557
+ },
1558
+ {
1559
+ name: "showLegend",
1560
+ type: "boolean",
1561
+ default: "auto (true when more than one series)"
1562
+ },
1563
+ {
1564
+ name: "showXAxis",
1565
+ type: "boolean",
1566
+ default: "true"
1567
+ },
1568
+ {
1569
+ name: "showYAxis",
1570
+ type: "boolean",
1571
+ default: "true"
1572
+ },
1573
+ {
1574
+ name: "showTooltip",
1575
+ type: "boolean",
1576
+ default: "true"
1577
+ },
1578
+ {
1579
+ name: "showPoints",
1580
+ type: "boolean",
1581
+ default: "false",
1582
+ note: "Always draw markers on line/area points."
1583
+ },
1584
+ {
1585
+ name: "yTicks",
1586
+ type: "number",
1587
+ default: "5"
1588
+ },
1589
+ {
1590
+ name: "valueFormat",
1591
+ type: "(value: number) => string",
1592
+ default: "locale integers for |value| >= 1000"
1593
+ },
1594
+ {
1595
+ name: "minY",
1596
+ type: "number",
1597
+ default: "auto"
1598
+ },
1599
+ {
1600
+ name: "maxY",
1601
+ type: "number",
1602
+ default: "auto"
1603
+ },
1604
+ {
1605
+ name: "animated",
1606
+ type: "boolean",
1607
+ default: "true",
1608
+ note: "Draw-in animation; disabled under prefers-reduced-motion."
1609
+ },
1610
+ {
1611
+ name: "loading",
1612
+ type: "boolean",
1613
+ default: "false"
1614
+ },
1615
+ {
1616
+ name: "ariaLabel",
1617
+ type: "string",
1618
+ default: "auto-generated summary"
1619
+ }
1620
+ ],
1621
+ events: [
1622
+ {
1623
+ name: "point-click",
1624
+ payload: "{ index: number; label: string; values: { name: string; value: number; color: string }[] }"
1625
+ }
1626
+ ],
1627
+ slots: [
1628
+ {
1629
+ name: "legend"
1630
+ },
1631
+ {
1632
+ name: "tooltip"
1633
+ },
1634
+ {
1635
+ name: "empty"
1636
+ }
1637
+ ],
1638
+ a11y: [
1639
+ "SVG is aria-hidden; a visually-hidden <table> mirrors the series data for assistive tech.",
1640
+ "Legend defaults on for multiple series so identity is not color-only.",
1641
+ "Provide a meaningful ariaLabel describing what the chart shows."
1642
+ ],
1643
+ behavior: [
1644
+ "colors_auto_assign_from_chart_palette: --tree-color-chart-1..8, cycling; per-series color overrides",
1645
+ "bar_zero_baseline: true",
1646
+ "line_domain_uses_nice_ticks: true",
1647
+ "hover_shows_crosshair_and_tooltip: true"
1648
+ ],
1649
+ selection: {
1650
+ keywords: [
1651
+ "chart",
1652
+ "graph",
1653
+ "line chart",
1654
+ "area chart",
1655
+ "bar chart",
1656
+ "plot",
1657
+ "dashboard",
1658
+ "time series",
1659
+ "analytics"
1660
+ ],
1661
+ useWhen: [
1662
+ "You need a line, area, or bar chart with axes, gridlines, legend, and a hover tooltip.",
1663
+ "One or more series share a set of category labels."
1664
+ ],
1665
+ avoidWhen: [
1666
+ "A single value tells the story \u2014 use TStat.",
1667
+ "You only need an inline trend with no axes \u2014 use TSparkline.",
1668
+ "The data is a part-of-whole breakdown \u2014 use TDonutChart."
1669
+ ],
1670
+ alternatives: [
1671
+ {
1672
+ component: "TSparkline",
1673
+ when: "The trend should be inline and axis-less."
1674
+ },
1675
+ {
1676
+ component: "TDonutChart",
1677
+ when: "The data is proportional (parts of a whole)."
1678
+ }
1679
+ ]
1680
+ },
1681
+ setup: {
1682
+ required: [],
1683
+ notes: [
1684
+ "Pass `series` as `{ name, data, color? }[]` and `labels` as a parallel `string[]` (one label per data index).",
1685
+ "Set `type` to `line`, `area`, or `bar`; use `stacked` only with `bar`.",
1686
+ "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.",
1687
+ "Pass `valueFormat: (n) => string` to format axis and tooltip values (currency, compact, etc.).",
1688
+ "No charting library or provider is required \u2014 the SVG and geometry are built in."
1689
+ ]
1690
+ },
1691
+ recipes: [
1692
+ "chart-surface",
1693
+ "metric-dashboard"
1694
+ ]
1695
+ },
1168
1696
  {
1169
1697
  name: "TCheckbox",
1170
1698
  aliases: [],
@@ -1648,9 +2176,14 @@ var treeui_catalog_default = {
1648
2176
  "The page shell should stay full-width while content stays readable."
1649
2177
  ],
1650
2178
  avoidWhen: [
1651
- "You need internal spacing between children."
2179
+ "You need internal spacing between children.",
2180
+ "You are laying out a whole screen \u2014 reach for TPage, which adds page padding and vertical rhythm."
1652
2181
  ],
1653
2182
  alternatives: [
2183
+ {
2184
+ component: "TPage",
2185
+ when: "You want a full page region with block padding and vertical spacing between sections, not just a width constraint."
2186
+ },
1654
2187
  {
1655
2188
  component: "TStack",
1656
2189
  when: "The main need is vertical or horizontal spacing."
@@ -2136,6 +2669,142 @@ var treeui_catalog_default = {
2136
2669
  },
2137
2670
  recipes: []
2138
2671
  },
2672
+ {
2673
+ name: "TDonutChart",
2674
+ aliases: [],
2675
+ package: "@treeui/vue",
2676
+ kind: "data-display",
2677
+ category: "Data Display",
2678
+ status: "available",
2679
+ 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.",
2680
+ sourceFile: "packages/vue/src/components/TDonutChart.vue",
2681
+ storyFile: "apps/docs/src/stories/DonutChart.stories.ts",
2682
+ storyTitle: "Components/Data Display/Donut Chart",
2683
+ manifestFile: "docs/ai/COMPONENTS/donut-chart.yaml",
2684
+ props: [
2685
+ {
2686
+ name: "segments",
2687
+ type: "TDonutSegment[]",
2688
+ default: "[]",
2689
+ note: "Each item is { label: string; value: number; color?: string }. Non-positive values are omitted."
2690
+ },
2691
+ {
2692
+ name: "size",
2693
+ type: "number",
2694
+ default: "200",
2695
+ note: "Square canvas in px."
2696
+ },
2697
+ {
2698
+ name: "thickness",
2699
+ type: "number",
2700
+ default: "26",
2701
+ note: "Ring thickness; 0 renders a full pie."
2702
+ },
2703
+ {
2704
+ name: "gap",
2705
+ type: "number",
2706
+ default: "2",
2707
+ note: "Gap between slices, in degrees."
2708
+ },
2709
+ {
2710
+ name: "rounded",
2711
+ type: "boolean",
2712
+ default: "false",
2713
+ note: "Round the ends of donut segments (widens the gap to make room). Best with a thinner ring."
2714
+ },
2715
+ {
2716
+ name: "showLegend",
2717
+ type: "boolean",
2718
+ default: "true"
2719
+ },
2720
+ {
2721
+ name: "showPercent",
2722
+ type: "boolean",
2723
+ default: "true"
2724
+ },
2725
+ {
2726
+ name: "centerLabel",
2727
+ type: "string",
2728
+ default: "Total",
2729
+ note: "Donut only; ignored for pie."
2730
+ },
2731
+ {
2732
+ name: "centerValue",
2733
+ type: "string | number",
2734
+ default: "auto (sum of values)"
2735
+ },
2736
+ {
2737
+ name: "valueFormat",
2738
+ type: "(value: number) => string",
2739
+ default: "locale integers for |value| >= 1000"
2740
+ },
2741
+ {
2742
+ name: "loading",
2743
+ type: "boolean",
2744
+ default: "false"
2745
+ },
2746
+ {
2747
+ name: "ariaLabel",
2748
+ type: "string",
2749
+ default: "auto-generated summary"
2750
+ }
2751
+ ],
2752
+ events: [],
2753
+ slots: [
2754
+ {
2755
+ name: "center"
2756
+ },
2757
+ {
2758
+ name: "empty"
2759
+ }
2760
+ ],
2761
+ a11y: [
2762
+ "Renders role=img with an aria-label summarizing the chart.",
2763
+ "Legend + percentages give a non-visual reading of each slice."
2764
+ ],
2765
+ behavior: [
2766
+ "colors_auto_assign_from_chart_palette: --tree-color-chart-1..8, cycling; per-segment color overrides",
2767
+ "hovering_a_slice_dims_the_rest_and_updates_center: true",
2768
+ "center_shows_total_or_the_hovered_segment: true"
2769
+ ],
2770
+ selection: {
2771
+ keywords: [
2772
+ "donut",
2773
+ "doughnut",
2774
+ "pie",
2775
+ "proportion",
2776
+ "share",
2777
+ "breakdown",
2778
+ "part of whole",
2779
+ "percentage"
2780
+ ],
2781
+ useWhen: [
2782
+ "You are showing how a total splits into a handful of categories.",
2783
+ "A donut center readout (total or hovered slice) is useful."
2784
+ ],
2785
+ avoidWhen: [
2786
+ "There are many categories or precise comparison matters \u2014 use TChart bars.",
2787
+ "The data is a trend over time \u2014 use TChart or TSparkline."
2788
+ ],
2789
+ alternatives: [
2790
+ {
2791
+ component: "TChart",
2792
+ when: "Precise magnitude comparison across categories is needed (bars)."
2793
+ }
2794
+ ]
2795
+ },
2796
+ setup: {
2797
+ required: [],
2798
+ notes: [
2799
+ "Pass `segments` as `{ label, value, color? }[]`; non-positive values are dropped.",
2800
+ "Set `thickness: 0` for a full pie; keep the default for a donut with a center readout.",
2801
+ "Use the `center` slot (donut only) for a custom center; otherwise it shows the total or the hovered slice."
2802
+ ]
2803
+ },
2804
+ recipes: [
2805
+ "metric-dashboard"
2806
+ ]
2807
+ },
2139
2808
  {
2140
2809
  name: "TDrawer",
2141
2810
  aliases: [],
@@ -2337,6 +3006,16 @@ var treeui_catalog_default = {
2337
3006
  name: "label",
2338
3007
  type: "string",
2339
3008
  default: ""
3009
+ },
3010
+ {
3011
+ name: "align",
3012
+ type: "TDropdownAlign",
3013
+ default: "start",
3014
+ values: [
3015
+ "start",
3016
+ "end"
3017
+ ],
3018
+ 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)."
2340
3019
  }
2341
3020
  ],
2342
3021
  events: [
@@ -2372,7 +3051,8 @@ var treeui_catalog_default = {
2372
3051
  behavior: [
2373
3052
  "outside_click_closes_menu: true",
2374
3053
  "focus_restores_on_close: true",
2375
- "chevron_rotates_when_open: true"
3054
+ "chevron_rotates_when_open: true",
3055
+ "menu_grows_to_fit_items_without_wrapping: true"
2376
3056
  ],
2377
3057
  selection: {
2378
3058
  keywords: [
@@ -2502,7 +3182,7 @@ var treeui_catalog_default = {
2502
3182
  kind: "form",
2503
3183
  category: "Form",
2504
3184
  status: "available",
2505
- purpose: "Composable dropzone and file picker with browse, drag-and-drop, and Ctrl+V paste support.",
3185
+ 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.",
2506
3186
  sourceFile: "packages/vue/src/components/TFileUpload.vue",
2507
3187
  storyFile: "apps/docs/src/stories/FileUpload.stories.ts",
2508
3188
  storyTitle: "Components/Form/FileUpload",
@@ -2526,12 +3206,14 @@ var treeui_catalog_default = {
2526
3206
  {
2527
3207
  name: "disabled",
2528
3208
  type: "boolean",
2529
- default: "false"
3209
+ default: "false",
3210
+ description: "The ONLY prop that gates interaction. Disables the dropzone, the native input, Clear all, and every per-row Remove/Retry."
2530
3211
  },
2531
3212
  {
2532
3213
  name: "loading",
2533
3214
  type: "boolean",
2534
- default: "false"
3215
+ default: "false",
3216
+ 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."
2535
3217
  },
2536
3218
  {
2537
3219
  name: "invalid",
@@ -2564,72 +3246,170 @@ var treeui_catalog_default = {
2564
3246
  default: "Drop files here"
2565
3247
  },
2566
3248
  {
2567
- name: "description",
3249
+ name: "description",
3250
+ type: "string",
3251
+ default: "Click to browse, drag and drop, or paste with Ctrl+V."
3252
+ },
3253
+ {
3254
+ name: "buttonLabel",
3255
+ type: "string",
3256
+ default: "Browse files"
3257
+ },
3258
+ {
3259
+ name: "filesLabel",
3260
+ type: "string",
3261
+ default: "Selected files"
3262
+ },
3263
+ {
3264
+ name: "paste",
3265
+ type: "boolean",
3266
+ default: "true"
3267
+ },
3268
+ {
3269
+ name: "drop",
3270
+ type: "boolean",
3271
+ default: "true"
3272
+ },
3273
+ {
3274
+ name: "showFileList",
3275
+ type: "boolean",
3276
+ default: "true"
3277
+ },
3278
+ {
3279
+ name: "uploadState",
3280
+ type: "TFileUploadStateSource",
3281
+ default: "none",
3282
+ 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."
3283
+ },
3284
+ {
3285
+ name: "dragLabel",
3286
+ type: "string",
3287
+ default: "Release to upload",
3288
+ 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."
3289
+ },
3290
+ {
3291
+ name: "dragRejectLabel",
2568
3292
  type: "string",
2569
- default: "Click to browse, drag and drop, or paste with Ctrl+V."
3293
+ default: "This file type is not accepted",
3294
+ 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."
2570
3295
  },
2571
3296
  {
2572
- name: "buttonLabel",
2573
- type: "string",
2574
- default: "Browse files"
3297
+ name: "thumbnails",
3298
+ type: "boolean",
3299
+ default: "true",
3300
+ description: "Mint object-URL previews and decode dimensions for image/* files. Set false when you always supply `state.thumbnailUrl` yourself."
2575
3301
  },
2576
3302
  {
2577
- name: "filesLabel",
3303
+ name: "maxThumbnailSize",
3304
+ type: "number | null",
3305
+ default: "10 * 1024 * 1024",
3306
+ description: "Skip auto-thumbnailing above this byte size. null removes the cap."
3307
+ },
3308
+ {
3309
+ name: "retryLabel",
2578
3310
  type: "string",
2579
- default: "Selected files"
3311
+ default: "Retry"
2580
3312
  },
2581
3313
  {
2582
- name: "paste",
2583
- type: "boolean",
2584
- default: "true"
3314
+ name: "resumeLabel",
3315
+ type: "(percent: number) => string",
3316
+ default: "(percent) => `Resume from ${percent}%`",
3317
+ description: "Receives Math.round(state.progress). Used only when state.resumable === true AND state.progress is a number."
2585
3318
  },
2586
3319
  {
2587
- name: "drop",
2588
- type: "boolean",
2589
- default: "true"
3320
+ name: "remainingTimeFormat",
3321
+ type: "(remainingMs: number) => string",
3322
+ default: "5s/60s/60min thresholds \u2014 see behavior.remaining_time_strings",
3323
+ description: "Formats state.remainingMs. Ignored for a row that supplies state.remainingLabel."
2590
3324
  },
2591
3325
  {
2592
- name: "showFileList",
2593
- type: "boolean",
2594
- default: "true"
3326
+ name: "statusLabels",
3327
+ type: "Partial<Record<TFileUploadStatus, string>>",
3328
+ default: "{}",
3329
+ description: "MERGED over the defaults { pending: Waiting, uploading: Uploading, paused: Paused, error: Upload failed, success: Uploaded } \u2014 pass only the keys you override."
2595
3330
  }
2596
3331
  ],
2597
3332
  events: [
2598
3333
  {
2599
- name: "update:modelValue"
3334
+ name: "update:modelValue",
3335
+ description: "Emitted with the next file list when files are added, removed, or cleared.",
3336
+ payload: "File[]"
3337
+ },
3338
+ {
3339
+ name: "files-accepted",
3340
+ description: "Emitted with only the newly accepted files, after accept/maxFiles/maxFileSize validation.",
3341
+ payload: "File[]"
2600
3342
  },
2601
3343
  {
2602
- name: "files-accepted"
3344
+ name: "files-rejected",
3345
+ description: "Pre-acceptance validation failures. Unrelated to transport errors, which you report through `uploadState`.",
3346
+ payload: "TFileUploadRejection[]"
2603
3347
  },
2604
3348
  {
2605
- name: "files-rejected"
3349
+ name: "retry",
3350
+ 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.",
3351
+ payload: "TFileUploadRetryPayload"
3352
+ },
3353
+ {
3354
+ name: "cancel",
3355
+ 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.",
3356
+ payload: "TFileUploadCancelPayload"
2606
3357
  }
2607
3358
  ],
2608
3359
  slots: [
2609
3360
  {
2610
- name: "icon"
3361
+ name: "icon",
3362
+ description: "Replaces the dropzone icon."
2611
3363
  },
2612
3364
  {
2613
- name: "default"
3365
+ name: "default",
3366
+ description: "Replaces the dropzone body. Receives `{ files, isDragActive, isDragReject, openFileDialog, clearFiles }`."
2614
3367
  },
2615
3368
  {
2616
- name: "file"
3369
+ name: "file",
3370
+ description: "Full row override. Receives all of TFileUploadFileSlotProps, including every derived label, so a custom row re-derives nothing."
2617
3371
  },
2618
3372
  {
2619
- name: "actions"
3373
+ name: "file-status",
3374
+ 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."
3375
+ },
3376
+ {
3377
+ name: "actions",
3378
+ description: "Footer actions. Receives `{ files, openFileDialog, clearFiles }` \u2014 no upload state."
2620
3379
  }
2621
3380
  ],
2622
3381
  a11y: [
2623
3382
  "dropzone_role: button",
2624
3383
  "keyboard_activation: Enter_and_Space_open_the_file_picker",
2625
3384
  "supports_paste_when_component_is_focused: true",
2626
- "file_feedback_announced_via_live_region: true"
3385
+ "file_feedback_announced_via_live_region: true",
3386
+ "aria_busy_on_dropzone_tracks_loading_prop: true",
3387
+ "aria_busy_on_row_tracks_that_rows_uploading_status: true",
3388
+ "per_row_progressbar_is_labelled_with_status_and_filename: e.g. 'Uploading hero.png' \u2014 every row's bar has a distinct accessible name.",
3389
+ "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.'",
3390
+ "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.",
3391
+ "percentages_are_never_announced: Percent and ETA live in non-live text; the value reaches assistive tech through the progressbar's aria-valuenow.",
3392
+ "retry_button_is_described_by_its_row_error: Announces 'Resume from 90%, Connection lost at 90%'.",
3393
+ "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.",
3394
+ "remove_and_retry_meet_the_44px_target: true"
2627
3395
  ],
2628
3396
  behavior: [
2629
3397
  "accepts_files_from_input_drop_and_paste: true",
2630
3398
  "multiple_false_replaces_existing_selection: true",
2631
3399
  "accept_maxFiles_and_maxFileSize_apply_to_all_entry_points: true",
2632
- "selected_files_can_be_removed_or_cleared: true"
3400
+ "selected_files_can_be_removed_or_cleared: true",
3401
+ "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`.",
3402
+ "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.",
3403
+ "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.",
3404
+ "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.",
3405
+ "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.",
3406
+ "drag_feedback_has_three_signals: Border, glow, and a text swap to `dragLabel` \u2014 all three fire together, including when files are already listed.",
3407
+ "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.",
3408
+ "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.",
3409
+ "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.",
3410
+ "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.",
3411
+ "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.",
3412
+ "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."
2633
3413
  ],
2634
3414
  selection: {
2635
3415
  keywords: [],
@@ -2872,7 +3652,8 @@ var treeui_catalog_default = {
2872
3652
  },
2873
3653
  recipes: [
2874
3654
  "date-range-form",
2875
- "stat-grid"
3655
+ "stat-grid",
3656
+ "metric-dashboard"
2876
3657
  ]
2877
3658
  },
2878
3659
  {
@@ -3683,7 +4464,7 @@ var treeui_catalog_default = {
3683
4464
  name: "items",
3684
4465
  type: "TNavMenuItem[]",
3685
4466
  default: "[]",
3686
- note: "Array of `{ label, value, shortLabel?, description?, icon?, to?, badge?, disabled? }`."
4467
+ note: "Array of `{ label, value, shortLabel?, description?, icon?, to?, badge?, disabled?, exact? }`."
3687
4468
  },
3688
4469
  {
3689
4470
  name: "size",
@@ -3704,6 +4485,12 @@ var treeui_catalog_default = {
3704
4485
  name: "disabled",
3705
4486
  type: "boolean",
3706
4487
  default: "false"
4488
+ },
4489
+ {
4490
+ name: "exact",
4491
+ type: "boolean",
4492
+ default: "true",
4493
+ 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`."
3707
4494
  }
3708
4495
  ],
3709
4496
  events: [
@@ -3762,10 +4549,11 @@ var treeui_catalog_default = {
3762
4549
  setup: {
3763
4550
  required: [],
3764
4551
  notes: [
3765
- "When nested inside `TSidebar`, `collapsed` can be inherited from the sidebar provider."
4552
+ "When nested inside `TSidebar` or `TAppShell`, `collapsed` can be inherited from the sidebar provider."
3766
4553
  ]
3767
4554
  },
3768
4555
  recipes: [
4556
+ "app-shell",
3769
4557
  "sidebar-shell"
3770
4558
  ]
3771
4559
  },
@@ -3876,6 +4664,193 @@ var treeui_catalog_default = {
3876
4664
  },
3877
4665
  recipes: []
3878
4666
  },
4667
+ {
4668
+ name: "TPage",
4669
+ aliases: [],
4670
+ package: "@treeui/vue",
4671
+ kind: "layout",
4672
+ category: "Layout",
4673
+ status: "available",
4674
+ purpose: "Semantic page region under the app shell: block padding, a centered max-width content column, and standardized vertical rhythm between sections.",
4675
+ sourceFile: "packages/vue/src/components/TPage.vue",
4676
+ storyFile: "apps/docs/src/stories/Page.stories.ts",
4677
+ storyTitle: "Components/Layout/Page",
4678
+ manifestFile: "docs/ai/COMPONENTS/page.yaml",
4679
+ props: [
4680
+ {
4681
+ name: "as",
4682
+ type: "string",
4683
+ default: "div"
4684
+ },
4685
+ {
4686
+ name: "width",
4687
+ type: "TContainerSize",
4688
+ default: "lg",
4689
+ values: [
4690
+ "sm",
4691
+ "md",
4692
+ "lg",
4693
+ "xl",
4694
+ "full"
4695
+ ],
4696
+ note: "Max-width of the centered content column. Mirrors TContainer sizes."
4697
+ },
4698
+ {
4699
+ name: "padded",
4700
+ type: "boolean",
4701
+ default: "true",
4702
+ note: "Adds vertical (block) padding on the region and horizontal padding on the inner column."
4703
+ },
4704
+ {
4705
+ name: "gap",
4706
+ type: "TPageGap",
4707
+ default: "lg",
4708
+ values: [
4709
+ "sm",
4710
+ "md",
4711
+ "lg"
4712
+ ],
4713
+ note: "Vertical spacing between direct children (e.g. a TPageHeader and content sections)."
4714
+ }
4715
+ ],
4716
+ events: [],
4717
+ slots: [
4718
+ {
4719
+ name: "default"
4720
+ }
4721
+ ],
4722
+ a11y: [
4723
+ "Structural wrapper only; landmark semantics come from `as` and the surrounding app shell (avoid nesting a second `main`)."
4724
+ ],
4725
+ behavior: [
4726
+ "centers_content_column_by_width: true",
4727
+ "applies_block_padding_when_padded: true",
4728
+ "spaces_children_vertically_by_gap: true"
4729
+ ],
4730
+ selection: {
4731
+ keywords: [
4732
+ "page",
4733
+ "page shell",
4734
+ "content column",
4735
+ "screen layout"
4736
+ ],
4737
+ useWhen: [
4738
+ "You want a standard screen region under the app shell with consistent max width, page padding, and vertical rhythm between sections."
4739
+ ],
4740
+ avoidWhen: [
4741
+ "You only need a bare width constraint with no page padding or child spacing."
4742
+ ],
4743
+ alternatives: [
4744
+ {
4745
+ component: "TContainer",
4746
+ when: "You just need a centered max-width wrapper without page-level block padding or child spacing."
4747
+ },
4748
+ {
4749
+ component: "TStack",
4750
+ when: "You only need to space a group of children, not lay out a whole page."
4751
+ }
4752
+ ]
4753
+ },
4754
+ setup: {
4755
+ required: [],
4756
+ notes: []
4757
+ },
4758
+ recipes: []
4759
+ },
4760
+ {
4761
+ name: "TPageHeader",
4762
+ aliases: [],
4763
+ package: "@treeui/vue",
4764
+ kind: "layout",
4765
+ category: "Layout",
4766
+ status: "available",
4767
+ purpose: "Canonical page title block: heading, optional subtitle, and a right-aligned actions area, so title hierarchy and alignment stay consistent across screens.",
4768
+ sourceFile: "packages/vue/src/components/TPageHeader.vue",
4769
+ storyFile: "apps/docs/src/stories/PageHeader.stories.ts",
4770
+ storyTitle: "Components/Layout/PageHeader",
4771
+ manifestFile: "docs/ai/COMPONENTS/page-header.yaml",
4772
+ props: [
4773
+ {
4774
+ name: "title",
4775
+ type: "string",
4776
+ default: "",
4777
+ note: "Rendered inside the heading element. Overridden by the `title` slot."
4778
+ },
4779
+ {
4780
+ name: "subtitle",
4781
+ type: "string",
4782
+ default: ""
4783
+ },
4784
+ {
4785
+ name: "level",
4786
+ type: "1 | 2 | 3 | 4 | 5 | 6",
4787
+ default: "1",
4788
+ note: "Heading level for the title element. Default `1` treats it as the page title; lower it when the page already has an h1."
4789
+ },
4790
+ {
4791
+ name: "as",
4792
+ type: "string",
4793
+ default: "header"
4794
+ }
4795
+ ],
4796
+ events: [],
4797
+ slots: [
4798
+ {
4799
+ name: "breadcrumb"
4800
+ },
4801
+ {
4802
+ name: "title"
4803
+ },
4804
+ {
4805
+ name: "subtitle"
4806
+ },
4807
+ {
4808
+ name: "actions"
4809
+ },
4810
+ {
4811
+ name: "default"
4812
+ }
4813
+ ],
4814
+ a11y: [
4815
+ "Title renders as a real heading element (`h1`\u2013`h6`) chosen by `level`; keep one h1 per page.",
4816
+ "Actions area holds standard focusable controls (e.g. TButton) \u2014 no custom roles added."
4817
+ ],
4818
+ behavior: [
4819
+ "title_and_subtitle_optional: true",
4820
+ "heading_level_controlled_by_level: true",
4821
+ "actions_area_right_aligned_and_wraps: true",
4822
+ "default_slot_renders_below_heading: true"
4823
+ ],
4824
+ selection: {
4825
+ keywords: [
4826
+ "page header",
4827
+ "title bar",
4828
+ "page title",
4829
+ "heading and actions"
4830
+ ],
4831
+ useWhen: [
4832
+ "A screen needs a title (and optional subtitle) with a right-aligned actions area in a consistent hierarchy."
4833
+ ],
4834
+ avoidWhen: [
4835
+ "You need cross-page hierarchy or navigation trails rather than a single page title."
4836
+ ],
4837
+ alternatives: [
4838
+ {
4839
+ component: "TBreadcrumb",
4840
+ when: "You are showing where the page sits in a hierarchy, not its title."
4841
+ },
4842
+ {
4843
+ component: "TNavbar",
4844
+ when: "You need a top application bar, not a per-page title block."
4845
+ }
4846
+ ]
4847
+ },
4848
+ setup: {
4849
+ required: [],
4850
+ notes: []
4851
+ },
4852
+ recipes: []
4853
+ },
3879
4854
  {
3880
4855
  name: "TPagination",
3881
4856
  aliases: [],
@@ -4592,7 +5567,8 @@ var treeui_catalog_default = {
4592
5567
  "check_icon_on_selected_option: true",
4593
5568
  "outside_click_closes_dropdown: true",
4594
5569
  "focus_restores_on_close: true",
4595
- "numeric_values_supported_without_string_coercion: true"
5570
+ "numeric_values_supported_without_string_coercion: true",
5571
+ "listbox_flips_above_trigger_when_space_below_is_insufficient: true"
4596
5572
  ],
4597
5573
  selection: {
4598
5574
  keywords: [
@@ -4625,7 +5601,9 @@ var treeui_catalog_default = {
4625
5601
  "Use `placeholder` when no selection has been made."
4626
5602
  ]
4627
5603
  },
4628
- recipes: []
5604
+ recipes: [
5605
+ "chart-surface"
5606
+ ]
4629
5607
  },
4630
5608
  {
4631
5609
  name: "TSelectableList",
@@ -4847,7 +5825,8 @@ var treeui_catalog_default = {
4847
5825
  "controlled_and_uncontrolled_collapsed_state: true",
4848
5826
  "provides_collapsed_state_to_descendants: true",
4849
5827
  "left_and_right_variants: true",
4850
- "exposes_slot_props_with_collapsed_state: true"
5828
+ "exposes_slot_props_with_collapsed_state: true",
5829
+ "collapsed_state_centers_header_and_footer_content: true"
4851
5830
  ],
4852
5831
  selection: {
4853
5832
  keywords: [
@@ -4872,7 +5851,8 @@ var treeui_catalog_default = {
4872
5851
  required: [],
4873
5852
  notes: [
4874
5853
  "Pair with `TNavMenu` for app-section navigation.",
4875
- "Use controlled `collapsed` state when the app shell persists layout preferences."
5854
+ "Use controlled `collapsed` state when the app shell persists layout preferences.",
5855
+ "Prefer `TAppShell` when you also want full-viewport sizing, a top header, and a responsive mobile drawer."
4876
5856
  ]
4877
5857
  },
4878
5858
  recipes: [
@@ -4942,6 +5922,129 @@ var treeui_catalog_default = {
4942
5922
  },
4943
5923
  recipes: []
4944
5924
  },
5925
+ {
5926
+ name: "TSparkline",
5927
+ aliases: [],
5928
+ package: "@treeui/vue",
5929
+ kind: "data-display",
5930
+ category: "Data Display",
5931
+ status: "available",
5932
+ purpose: "Tiny, axis-less inline trend chart (line, area, or bar) sized for stat tiles, table cells, and running text.",
5933
+ sourceFile: "packages/vue/src/components/TSparkline.vue",
5934
+ storyFile: "apps/docs/src/stories/Sparkline.stories.ts",
5935
+ storyTitle: "Components/Data Display/Sparkline",
5936
+ manifestFile: "docs/ai/COMPONENTS/sparkline.yaml",
5937
+ props: [
5938
+ {
5939
+ name: "data",
5940
+ type: "number[]",
5941
+ default: "[]"
5942
+ },
5943
+ {
5944
+ name: "type",
5945
+ type: "line | area | bar",
5946
+ default: "line"
5947
+ },
5948
+ {
5949
+ name: "color",
5950
+ type: "string",
5951
+ default: "var(--tree-color-chart-1)",
5952
+ note: "Any CSS color."
5953
+ },
5954
+ {
5955
+ name: "width",
5956
+ type: "number",
5957
+ default: "120"
5958
+ },
5959
+ {
5960
+ name: "height",
5961
+ type: "number",
5962
+ default: "32"
5963
+ },
5964
+ {
5965
+ name: "smooth",
5966
+ type: "boolean",
5967
+ default: "false",
5968
+ note: "Curved line/area."
5969
+ },
5970
+ {
5971
+ name: "strokeWidth",
5972
+ type: "number",
5973
+ default: "2",
5974
+ note: "Line/area only."
5975
+ },
5976
+ {
5977
+ name: "min",
5978
+ type: "number",
5979
+ default: "data min"
5980
+ },
5981
+ {
5982
+ name: "max",
5983
+ type: "number",
5984
+ default: "data max"
5985
+ },
5986
+ {
5987
+ name: "showLastPoint",
5988
+ type: "boolean",
5989
+ default: "false",
5990
+ note: "Marker on the final point (line/area)."
5991
+ },
5992
+ {
5993
+ name: "ariaLabel",
5994
+ type: "string",
5995
+ default: "Trend sparkline"
5996
+ }
5997
+ ],
5998
+ events: [],
5999
+ slots: [],
6000
+ a11y: [
6001
+ "Renders role=img with an aria-label; pass a specific one for meaningful trends.",
6002
+ "Pair with a visible number \u2014 a sparkline supplements a value, it does not replace it."
6003
+ ],
6004
+ behavior: [
6005
+ "no_axes_or_legend: true",
6006
+ "flat_series_renders_centered: true",
6007
+ "scales_to_intrinsic_width_and_height: true"
6008
+ ],
6009
+ selection: {
6010
+ keywords: [
6011
+ "sparkline",
6012
+ "mini chart",
6013
+ "inline chart",
6014
+ "micro trend",
6015
+ "trend line",
6016
+ "table chart"
6017
+ ],
6018
+ useWhen: [
6019
+ "You want a compact trend beside a value, in a table cell, or inline in text.",
6020
+ "No axes, legend, or tooltip are required."
6021
+ ],
6022
+ avoidWhen: [
6023
+ "You need axes, multiple labeled series, or a tooltip \u2014 use TChart."
6024
+ ],
6025
+ alternatives: [
6026
+ {
6027
+ component: "TChart",
6028
+ when: "The chart needs axes, a legend, or a tooltip."
6029
+ },
6030
+ {
6031
+ component: "TStat",
6032
+ when: "A single value with a trend badge is enough."
6033
+ }
6034
+ ]
6035
+ },
6036
+ setup: {
6037
+ required: [],
6038
+ notes: [
6039
+ "Pass `data` as `number[]`; there are no axes, legend, or tooltip by design.",
6040
+ "Size it with `width`/`height`; use it inside `TStat`, table cells, or text.",
6041
+ "Always keep a visible number nearby \u2014 a sparkline supplements a value, it does not replace it."
6042
+ ]
6043
+ },
6044
+ recipes: [
6045
+ "metric-dashboard"
6046
+ ]
6047
+ },
4945
6048
  {
4946
6049
  name: "TSpinner",
4947
6050
  aliases: [],
@@ -5195,6 +6298,10 @@ var treeui_catalog_default = {
5195
6298
  {
5196
6299
  component: "TCard",
5197
6300
  when: "The content is not a metric."
6301
+ },
6302
+ {
6303
+ component: "TSparkline",
6304
+ when: "You want to show the metric's trend as an inline mini-chart."
5198
6305
  }
5199
6306
  ]
5200
6307
  },
@@ -5203,7 +6310,8 @@ var treeui_catalog_default = {
5203
6310
  notes: []
5204
6311
  },
5205
6312
  recipes: [
5206
- "stat-grid"
6313
+ "stat-grid",
6314
+ "metric-dashboard"
5207
6315
  ]
5208
6316
  },
5209
6317
  {