@treeui/mcp 0.3.0 → 0.4.1

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-07-15T22:47:49.302Z",
50
+ generatedAt: "2026-07-18T19:02:09.022Z",
51
51
  packageName: "@treeui/vue",
52
- packageVersion: "0.13.0",
52
+ packageVersion: "0.15.0",
53
53
  decisionGuides: [
54
54
  {
55
55
  id: "single-choice",
@@ -993,6 +993,22 @@ var treeui_catalog_default = {
993
993
  type: "boolean",
994
994
  default: "true"
995
995
  },
996
+ {
997
+ name: "showCollapseButton",
998
+ type: "boolean",
999
+ default: "true",
1000
+ note: "Renders the built-in collapse toggle in the sidebar footer. Desktop only, and only when `collapsible` is set \u2014 no need to wire your own."
1001
+ },
1002
+ {
1003
+ name: "collapseLabel",
1004
+ type: "string",
1005
+ default: "Collapse sidebar"
1006
+ },
1007
+ {
1008
+ name: "expandLabel",
1009
+ type: "string",
1010
+ default: "Expand sidebar"
1011
+ },
996
1012
  {
997
1013
  name: "menuLabel",
998
1014
  type: "string",
@@ -1027,14 +1043,26 @@ var treeui_catalog_default = {
1027
1043
  {
1028
1044
  name: "header"
1029
1045
  },
1046
+ {
1047
+ name: "header-start"
1048
+ },
1049
+ {
1050
+ name: "sidebar-header"
1051
+ },
1030
1052
  {
1031
1053
  name: "sidebar"
1032
1054
  },
1055
+ {
1056
+ name: "sidebar-footer"
1057
+ },
1033
1058
  {
1034
1059
  name: "default"
1035
1060
  },
1036
1061
  {
1037
1062
  name: "menu-icon"
1063
+ },
1064
+ {
1065
+ name: "collapse-icon"
1038
1066
  }
1039
1067
  ],
1040
1068
  a11y: [
@@ -1062,7 +1090,10 @@ var treeui_catalog_default = {
1062
1090
  "left_and_right_variants: true",
1063
1091
  "locks_body_scroll_while_drawer_open: true",
1064
1092
  "overlay_click_closes_when_enabled: true",
1065
- "exposes_slot_props_with_mobile_collapsed_and_toggles: true"
1093
+ "exposes_slot_props_with_mobile_collapsed_and_toggles: true",
1094
+ "sidebar_footer_pinned_to_bottom: true",
1095
+ "sidebar_header_and_footer_inset_to_align_with_nav_icons: true",
1096
+ "header_mirrors_shell_columns_when_header_start_is_used: true"
1066
1097
  ],
1067
1098
  selection: {
1068
1099
  keywords: [
@@ -1334,15 +1365,17 @@ var treeui_catalog_default = {
1334
1365
  },
1335
1366
  {
1336
1367
  name: "variant",
1337
- type: "TVariant",
1368
+ type: "TVariant | brand",
1338
1369
  default: "solid",
1339
1370
  values: [
1340
1371
  "solid",
1341
1372
  "outline",
1342
1373
  "ghost",
1343
1374
  "soft",
1344
- "danger"
1345
- ]
1375
+ "danger",
1376
+ "brand"
1377
+ ],
1378
+ 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."
1346
1379
  },
1347
1380
  {
1348
1381
  name: "size",
@@ -1369,6 +1402,18 @@ var treeui_catalog_default = {
1369
1402
  name: "type",
1370
1403
  type: "button | submit | reset",
1371
1404
  default: "button"
1405
+ },
1406
+ {
1407
+ name: "block",
1408
+ type: "boolean",
1409
+ default: "false",
1410
+ note: "Stretches the button to the full width of its container. Useful for menu rows and mobile CTAs."
1411
+ },
1412
+ {
1413
+ name: "align",
1414
+ type: "start | center | end",
1415
+ default: "center",
1416
+ note: "Content alignment; only meaningful together with `block`. Use `start` for left-aligned icon+label menu rows."
1372
1417
  }
1373
1418
  ],
1374
1419
  events: [
@@ -1434,7 +1479,6 @@ var treeui_catalog_default = {
1434
1479
  values: [
1435
1480
  "outline",
1436
1481
  "soft",
1437
- "solid",
1438
1482
  "inset"
1439
1483
  ]
1440
1484
  },
@@ -1473,7 +1517,8 @@ var treeui_catalog_default = {
1473
1517
  a11y: [],
1474
1518
  behavior: [
1475
1519
  "body_wrapper_class: t-card__body",
1476
- "solid_variant_rescopes_color_tokens_for_nested_content: true"
1520
+ 'variant_effects: {"outline":"Base surface with a default border. The styling baseline \u2014 the `t-card--outline` class is emitted but carries no CSS rule of its own.","soft":"Subtle tinted surface (bg-subtle), same border and shadow as outline.","inset":"Same tinted surface as soft, but with no shadow, so it reads as recessed inside another surface."}',
1521
+ "note: No card variant inverts its surface, so nested content inherits the normal color tokens. For a high-emphasis card, use a brand-colored border rather than an inverted background."
1477
1522
  ],
1478
1523
  selection: {
1479
1524
  keywords: [
@@ -1802,6 +1847,92 @@ var treeui_catalog_default = {
1802
1847
  },
1803
1848
  recipes: []
1804
1849
  },
1850
+ {
1851
+ name: "TColorSwatch",
1852
+ aliases: [],
1853
+ package: "@treeui/vue",
1854
+ kind: "form",
1855
+ category: "Data Entry",
1856
+ status: "available",
1857
+ purpose: "Pick a colour from a row of preset swatches, with an optional native colour input for an arbitrary value. Built for accent/brand pickers.",
1858
+ sourceFile: "packages/vue/src/components/TColorSwatch.vue",
1859
+ storyFile: "apps/docs/src/stories/ColorSwatch.stories.ts",
1860
+ storyTitle: "Components/Data Entry/ColorSwatch",
1861
+ manifestFile: "docs/ai/COMPONENTS/color-swatch.yaml",
1862
+ props: [
1863
+ {
1864
+ name: "modelValue",
1865
+ type: "string",
1866
+ default: "undefined",
1867
+ note: "Selected colour as a hex string. Matching is case-insensitive."
1868
+ },
1869
+ {
1870
+ name: "options",
1871
+ type: "TColorSwatchOption[]",
1872
+ default: "[]",
1873
+ note: "Preset swatches, each `{ label, value }`. `label` becomes the button's accessible name."
1874
+ },
1875
+ {
1876
+ name: "allowCustom",
1877
+ type: "boolean",
1878
+ default: "false",
1879
+ note: 'Appends a native `<input type="color">` for values outside the presets.'
1880
+ },
1881
+ {
1882
+ name: "size",
1883
+ type: "TSize",
1884
+ default: "md",
1885
+ values: [
1886
+ "sm",
1887
+ "md",
1888
+ "lg"
1889
+ ]
1890
+ },
1891
+ {
1892
+ name: "label",
1893
+ type: "string",
1894
+ default: "Color",
1895
+ note: 'Accessible label for the swatch group (`role="group"`).'
1896
+ },
1897
+ {
1898
+ name: "customLabel",
1899
+ type: "string",
1900
+ default: "Custom color"
1901
+ },
1902
+ {
1903
+ name: "disabled",
1904
+ type: "boolean",
1905
+ default: "false"
1906
+ }
1907
+ ],
1908
+ events: [
1909
+ {
1910
+ name: "update:modelValue"
1911
+ }
1912
+ ],
1913
+ slots: [],
1914
+ a11y: [
1915
+ 'Wrapper is `role="group"` labelled by `label`.',
1916
+ "Each swatch is a native button with `aria-pressed` reflecting selection and `aria-label` from the option label.",
1917
+ "Selection is conveyed by an outline (not colour alone), since the swatch's own colour is the content."
1918
+ ],
1919
+ behavior: [
1920
+ "emits_on_preset_click: true",
1921
+ "emits_on_custom_input: true",
1922
+ "blocks_interaction_when_disabled: true"
1923
+ ],
1924
+ selection: {
1925
+ keywords: [],
1926
+ useWhen: [],
1927
+ avoidWhen: [],
1928
+ alternatives: []
1929
+ },
1930
+ setup: {
1931
+ required: [],
1932
+ notes: []
1933
+ },
1934
+ recipes: []
1935
+ },
1805
1936
  {
1806
1937
  name: "TCombobox",
1807
1938
  aliases: [],
@@ -2187,9 +2318,14 @@ var treeui_catalog_default = {
2187
2318
  "The page shell should stay full-width while content stays readable."
2188
2319
  ],
2189
2320
  avoidWhen: [
2190
- "You need internal spacing between children."
2321
+ "You need internal spacing between children.",
2322
+ "You are laying out a whole screen \u2014 reach for TPage, which adds page padding and vertical rhythm."
2191
2323
  ],
2192
2324
  alternatives: [
2325
+ {
2326
+ component: "TPage",
2327
+ when: "You want a full page region with block padding and vertical spacing between sections, not just a width constraint."
2328
+ },
2193
2329
  {
2194
2330
  component: "TStack",
2195
2331
  when: "The main need is vertical or horizontal spacing."
@@ -3188,7 +3324,7 @@ var treeui_catalog_default = {
3188
3324
  kind: "form",
3189
3325
  category: "Form",
3190
3326
  status: "available",
3191
- purpose: "Composable dropzone and file picker with browse, drag-and-drop, and Ctrl+V paste support.",
3327
+ 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.",
3192
3328
  sourceFile: "packages/vue/src/components/TFileUpload.vue",
3193
3329
  storyFile: "apps/docs/src/stories/FileUpload.stories.ts",
3194
3330
  storyTitle: "Components/Form/FileUpload",
@@ -3212,12 +3348,14 @@ var treeui_catalog_default = {
3212
3348
  {
3213
3349
  name: "disabled",
3214
3350
  type: "boolean",
3215
- default: "false"
3351
+ default: "false",
3352
+ description: "The ONLY prop that gates interaction. Disables the dropzone, the native input, Clear all, and every per-row Remove/Retry."
3216
3353
  },
3217
3354
  {
3218
3355
  name: "loading",
3219
3356
  type: "boolean",
3220
- default: "false"
3357
+ default: "false",
3358
+ 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."
3221
3359
  },
3222
3360
  {
3223
3361
  name: "invalid",
@@ -3278,44 +3416,142 @@ var treeui_catalog_default = {
3278
3416
  name: "showFileList",
3279
3417
  type: "boolean",
3280
3418
  default: "true"
3419
+ },
3420
+ {
3421
+ name: "uploadState",
3422
+ type: "TFileUploadStateSource",
3423
+ default: "none",
3424
+ 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."
3425
+ },
3426
+ {
3427
+ name: "dragLabel",
3428
+ type: "string",
3429
+ default: "Release to upload",
3430
+ 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."
3431
+ },
3432
+ {
3433
+ name: "dragRejectLabel",
3434
+ type: "string",
3435
+ default: "This file type is not accepted",
3436
+ 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."
3437
+ },
3438
+ {
3439
+ name: "thumbnails",
3440
+ type: "boolean",
3441
+ default: "true",
3442
+ description: "Mint object-URL previews and decode dimensions for image/* files. Set false when you always supply `state.thumbnailUrl` yourself."
3443
+ },
3444
+ {
3445
+ name: "maxThumbnailSize",
3446
+ type: "number | null",
3447
+ default: "10 * 1024 * 1024",
3448
+ description: "Skip auto-thumbnailing above this byte size. null removes the cap."
3449
+ },
3450
+ {
3451
+ name: "retryLabel",
3452
+ type: "string",
3453
+ default: "Retry"
3454
+ },
3455
+ {
3456
+ name: "resumeLabel",
3457
+ type: "(percent: number) => string",
3458
+ default: "(percent) => `Resume from ${percent}%`",
3459
+ description: "Receives Math.round(state.progress). Used only when state.resumable === true AND state.progress is a number."
3460
+ },
3461
+ {
3462
+ name: "remainingTimeFormat",
3463
+ type: "(remainingMs: number) => string",
3464
+ default: "5s/60s/60min thresholds \u2014 see behavior.remaining_time_strings",
3465
+ description: "Formats state.remainingMs. Ignored for a row that supplies state.remainingLabel."
3466
+ },
3467
+ {
3468
+ name: "statusLabels",
3469
+ type: "Partial<Record<TFileUploadStatus, string>>",
3470
+ default: "{}",
3471
+ description: "MERGED over the defaults { pending: Waiting, uploading: Uploading, paused: Paused, error: Upload failed, success: Uploaded } \u2014 pass only the keys you override."
3281
3472
  }
3282
3473
  ],
3283
3474
  events: [
3284
3475
  {
3285
- name: "update:modelValue"
3476
+ name: "update:modelValue",
3477
+ description: "Emitted with the next file list when files are added, removed, or cleared.",
3478
+ payload: "File[]"
3479
+ },
3480
+ {
3481
+ name: "files-accepted",
3482
+ description: "Emitted with only the newly accepted files, after accept/maxFiles/maxFileSize validation.",
3483
+ payload: "File[]"
3484
+ },
3485
+ {
3486
+ name: "files-rejected",
3487
+ description: "Pre-acceptance validation failures. Unrelated to transport errors, which you report through `uploadState`.",
3488
+ payload: "TFileUploadRejection[]"
3286
3489
  },
3287
3490
  {
3288
- name: "files-accepted"
3491
+ name: "retry",
3492
+ 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.",
3493
+ payload: "TFileUploadRetryPayload"
3289
3494
  },
3290
3495
  {
3291
- name: "files-rejected"
3496
+ name: "cancel",
3497
+ 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.",
3498
+ payload: "TFileUploadCancelPayload"
3292
3499
  }
3293
3500
  ],
3294
3501
  slots: [
3295
3502
  {
3296
- name: "icon"
3503
+ name: "icon",
3504
+ description: "Replaces the dropzone icon."
3297
3505
  },
3298
3506
  {
3299
- name: "default"
3507
+ name: "default",
3508
+ description: "Replaces the dropzone body. Receives `{ files, isDragActive, isDragReject, openFileDialog, clearFiles }`."
3300
3509
  },
3301
3510
  {
3302
- name: "file"
3511
+ name: "file",
3512
+ description: "Full row override. Receives all of TFileUploadFileSlotProps, including every derived label, so a custom row re-derives nothing."
3303
3513
  },
3304
3514
  {
3305
- name: "actions"
3515
+ name: "file-status",
3516
+ 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."
3517
+ },
3518
+ {
3519
+ name: "actions",
3520
+ description: "Footer actions. Receives `{ files, openFileDialog, clearFiles }` \u2014 no upload state."
3306
3521
  }
3307
3522
  ],
3308
3523
  a11y: [
3309
3524
  "dropzone_role: button",
3310
3525
  "keyboard_activation: Enter_and_Space_open_the_file_picker",
3311
3526
  "supports_paste_when_component_is_focused: true",
3312
- "file_feedback_announced_via_live_region: true"
3527
+ "file_feedback_announced_via_live_region: true",
3528
+ "aria_busy_on_dropzone_tracks_loading_prop: true",
3529
+ "aria_busy_on_row_tracks_that_rows_uploading_status: true",
3530
+ "per_row_progressbar_is_labelled_with_status_and_filename: e.g. 'Uploading hero.png' \u2014 every row's bar has a distinct accessible name.",
3531
+ "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.'",
3532
+ "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.",
3533
+ "percentages_are_never_announced: Percent and ETA live in non-live text; the value reaches assistive tech through the progressbar's aria-valuenow.",
3534
+ "retry_button_is_described_by_its_row_error: Announces 'Resume from 90%, Connection lost at 90%'.",
3535
+ "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.",
3536
+ "remove_and_retry_meet_the_44px_target: true"
3313
3537
  ],
3314
3538
  behavior: [
3315
3539
  "accepts_files_from_input_drop_and_paste: true",
3316
3540
  "multiple_false_replaces_existing_selection: true",
3317
3541
  "accept_maxFiles_and_maxFileSize_apply_to_all_entry_points: true",
3318
- "selected_files_can_be_removed_or_cleared: true"
3542
+ "selected_files_can_be_removed_or_cleared: true",
3543
+ "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`.",
3544
+ "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.",
3545
+ "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.",
3546
+ "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.",
3547
+ "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.",
3548
+ "drag_feedback_has_three_signals: Border, glow, and a text swap to `dragLabel` \u2014 all three fire together, including when files are already listed.",
3549
+ "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.",
3550
+ "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.",
3551
+ "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.",
3552
+ "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.",
3553
+ "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.",
3554
+ "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."
3319
3555
  ],
3320
3556
  selection: {
3321
3557
  keywords: [],
@@ -3680,6 +3916,20 @@ var treeui_catalog_default = {
3680
3916
  "lg"
3681
3917
  ]
3682
3918
  },
3919
+ {
3920
+ name: "width",
3921
+ type: "TFieldWidth",
3922
+ default: "full",
3923
+ values: [
3924
+ "xs",
3925
+ "sm",
3926
+ "md",
3927
+ "lg",
3928
+ "xl",
3929
+ "full"
3930
+ ],
3931
+ note: "Inline-size cap (max-inline-size). Fluid by default; use instead of an inline width style."
3932
+ },
3683
3933
  {
3684
3934
  name: "disabled",
3685
3935
  type: "boolean",
@@ -3764,6 +4014,43 @@ var treeui_catalog_default = {
3764
4014
  "tag-input"
3765
4015
  ]
3766
4016
  },
4017
+ {
4018
+ name: "TKbd",
4019
+ aliases: [],
4020
+ package: "@treeui/vue",
4021
+ kind: "primitive",
4022
+ category: "Data Display",
4023
+ status: "available",
4024
+ purpose: "Keyboard-key hint (e.g. \u2318K, Esc, Ctrl) rendered as a native <kbd> with the library's chip styling.",
4025
+ sourceFile: "packages/vue/src/components/TKbd.vue",
4026
+ storyFile: "apps/docs/src/stories/Kbd.stories.ts",
4027
+ storyTitle: "Components/Data Display/Kbd",
4028
+ manifestFile: "docs/ai/COMPONENTS/kbd.yaml",
4029
+ props: [],
4030
+ events: [],
4031
+ slots: [
4032
+ {
4033
+ name: "default"
4034
+ }
4035
+ ],
4036
+ a11y: [
4037
+ "Uses the native <kbd> element so assistive tech announces it as keyboard input."
4038
+ ],
4039
+ behavior: [
4040
+ "renders_native_kbd_element: true"
4041
+ ],
4042
+ selection: {
4043
+ keywords: [],
4044
+ useWhen: [],
4045
+ avoidWhen: [],
4046
+ alternatives: []
4047
+ },
4048
+ setup: {
4049
+ required: [],
4050
+ notes: []
4051
+ },
4052
+ recipes: []
4053
+ },
3767
4054
  {
3768
4055
  name: "TLink",
3769
4056
  aliases: [],
@@ -4370,7 +4657,7 @@ var treeui_catalog_default = {
4370
4657
  name: "items",
4371
4658
  type: "TNavMenuItem[]",
4372
4659
  default: "[]",
4373
- note: "Array of `{ label, value, shortLabel?, description?, icon?, to?, badge?, disabled? }`."
4660
+ note: "Array of `{ label, value, shortLabel?, description?, icon?, to?, badge?, disabled?, exact? }`."
4374
4661
  },
4375
4662
  {
4376
4663
  name: "size",
@@ -4391,6 +4678,12 @@ var treeui_catalog_default = {
4391
4678
  name: "disabled",
4392
4679
  type: "boolean",
4393
4680
  default: "false"
4681
+ },
4682
+ {
4683
+ name: "exact",
4684
+ type: "boolean",
4685
+ default: "true",
4686
+ 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`."
4394
4687
  }
4395
4688
  ],
4396
4689
  events: [
@@ -4565,75 +4858,262 @@ var treeui_catalog_default = {
4565
4858
  recipes: []
4566
4859
  },
4567
4860
  {
4568
- name: "TPagination",
4861
+ name: "TPage",
4569
4862
  aliases: [],
4570
4863
  package: "@treeui/vue",
4571
- kind: "primitive",
4572
- category: "Navigation",
4864
+ kind: "layout",
4865
+ category: "Layout",
4573
4866
  status: "available",
4574
- purpose: "Page controls with previous/next navigation, numbered page buttons, and ellipsis for large page ranges. Supports v-model for the active page.\n",
4575
- sourceFile: "packages/vue/src/components/TPagination.vue",
4576
- storyFile: "apps/docs/src/stories/Pagination.stories.ts",
4577
- storyTitle: "Components/Navigation/Pagination",
4578
- manifestFile: "docs/ai/COMPONENTS/pagination.yaml",
4867
+ purpose: "Semantic page region under the app shell: block padding, a centered max-width content column, and standardized vertical rhythm between sections.",
4868
+ sourceFile: "packages/vue/src/components/TPage.vue",
4869
+ storyFile: "apps/docs/src/stories/Page.stories.ts",
4870
+ storyTitle: "Components/Layout/Page",
4871
+ manifestFile: "docs/ai/COMPONENTS/page.yaml",
4579
4872
  props: [
4580
4873
  {
4581
- name: "modelValue",
4582
- type: "number",
4583
- default: "1",
4584
- description: "Current active page (1-based). Use with v-model."
4585
- },
4586
- {
4587
- name: "totalPages",
4588
- type: "number",
4589
- required: true,
4590
- description: "Total number of pages."
4591
- },
4592
- {
4593
- name: "siblings",
4594
- type: "number",
4595
- default: "1",
4596
- description: "Number of sibling page buttons shown around the current page."
4874
+ name: "as",
4875
+ type: "string",
4876
+ default: "div"
4597
4877
  },
4598
4878
  {
4599
- name: "size",
4600
- type: "TSize",
4601
- default: "md",
4879
+ name: "width",
4880
+ type: "TContainerSize",
4881
+ default: "lg",
4602
4882
  values: [
4603
4883
  "sm",
4604
4884
  "md",
4605
- "lg"
4885
+ "lg",
4886
+ "xl",
4887
+ "full"
4606
4888
  ],
4607
- description: "Size variant for the pagination controls."
4889
+ note: "Max-width of the centered content column. Mirrors TContainer sizes."
4608
4890
  },
4609
4891
  {
4610
- name: "disabled",
4892
+ name: "padded",
4611
4893
  type: "boolean",
4612
- default: "false",
4613
- description: "Disables all pagination controls."
4894
+ default: "true",
4895
+ note: "Adds vertical (block) padding on the region and horizontal padding on the inner column."
4614
4896
  },
4615
4897
  {
4616
- name: "label",
4617
- type: "string",
4618
- default: "Pagination",
4619
- description: "Accessible label for the nav element."
4620
- }
4621
- ],
4622
- events: [
4623
- {
4624
- name: "update:modelValue",
4625
- description: "Emitted when the active page changes. Enables v-model binding.",
4626
- payload: "[page: number]"
4898
+ name: "gap",
4899
+ type: "TPageGap",
4900
+ default: "lg",
4901
+ values: [
4902
+ "sm",
4903
+ "md",
4904
+ "lg"
4905
+ ],
4906
+ note: "Vertical spacing between direct children (e.g. a TPageHeader and content sections)."
4627
4907
  }
4628
4908
  ],
4909
+ events: [],
4629
4910
  slots: [
4630
4911
  {
4631
- name: "previous",
4632
- description: "Custom content for the previous button."
4633
- },
4634
- {
4635
- name: "next",
4636
- description: "Custom content for the next button."
4912
+ name: "default"
4913
+ }
4914
+ ],
4915
+ a11y: [
4916
+ "Structural wrapper only; landmark semantics come from `as` and the surrounding app shell (avoid nesting a second `main`)."
4917
+ ],
4918
+ behavior: [
4919
+ "centers_content_column_by_width: true",
4920
+ "applies_block_padding_when_padded: true",
4921
+ "spaces_children_vertically_by_gap: true"
4922
+ ],
4923
+ selection: {
4924
+ keywords: [
4925
+ "page",
4926
+ "page shell",
4927
+ "content column",
4928
+ "screen layout"
4929
+ ],
4930
+ useWhen: [
4931
+ "You want a standard screen region under the app shell with consistent max width, page padding, and vertical rhythm between sections."
4932
+ ],
4933
+ avoidWhen: [
4934
+ "You only need a bare width constraint with no page padding or child spacing."
4935
+ ],
4936
+ alternatives: [
4937
+ {
4938
+ component: "TContainer",
4939
+ when: "You just need a centered max-width wrapper without page-level block padding or child spacing."
4940
+ },
4941
+ {
4942
+ component: "TStack",
4943
+ when: "You only need to space a group of children, not lay out a whole page."
4944
+ }
4945
+ ]
4946
+ },
4947
+ setup: {
4948
+ required: [],
4949
+ notes: []
4950
+ },
4951
+ recipes: []
4952
+ },
4953
+ {
4954
+ name: "TPageHeader",
4955
+ aliases: [],
4956
+ package: "@treeui/vue",
4957
+ kind: "layout",
4958
+ category: "Layout",
4959
+ status: "available",
4960
+ purpose: "Canonical page title block: heading, optional subtitle, and a right-aligned actions area, so title hierarchy and alignment stay consistent across screens.",
4961
+ sourceFile: "packages/vue/src/components/TPageHeader.vue",
4962
+ storyFile: "apps/docs/src/stories/PageHeader.stories.ts",
4963
+ storyTitle: "Components/Layout/PageHeader",
4964
+ manifestFile: "docs/ai/COMPONENTS/page-header.yaml",
4965
+ props: [
4966
+ {
4967
+ name: "title",
4968
+ type: "string",
4969
+ default: "",
4970
+ note: "Rendered inside the heading element. Overridden by the `title` slot."
4971
+ },
4972
+ {
4973
+ name: "subtitle",
4974
+ type: "string",
4975
+ default: ""
4976
+ },
4977
+ {
4978
+ name: "level",
4979
+ type: "1 | 2 | 3 | 4 | 5 | 6",
4980
+ default: "1",
4981
+ note: "Heading level for the title element. Default `1` treats it as the page title; lower it when the page already has an h1."
4982
+ },
4983
+ {
4984
+ name: "as",
4985
+ type: "string",
4986
+ default: "header"
4987
+ }
4988
+ ],
4989
+ events: [],
4990
+ slots: [
4991
+ {
4992
+ name: "breadcrumb"
4993
+ },
4994
+ {
4995
+ name: "title"
4996
+ },
4997
+ {
4998
+ name: "subtitle"
4999
+ },
5000
+ {
5001
+ name: "actions"
5002
+ },
5003
+ {
5004
+ name: "default"
5005
+ }
5006
+ ],
5007
+ a11y: [
5008
+ "Title renders as a real heading element (`h1`\u2013`h6`) chosen by `level`; keep one h1 per page.",
5009
+ "Actions area holds standard focusable controls (e.g. TButton) \u2014 no custom roles added."
5010
+ ],
5011
+ behavior: [
5012
+ "title_and_subtitle_optional: true",
5013
+ "heading_level_controlled_by_level: true",
5014
+ "actions_area_right_aligned_and_wraps: true",
5015
+ "default_slot_renders_below_heading: true"
5016
+ ],
5017
+ selection: {
5018
+ keywords: [
5019
+ "page header",
5020
+ "title bar",
5021
+ "page title",
5022
+ "heading and actions"
5023
+ ],
5024
+ useWhen: [
5025
+ "A screen needs a title (and optional subtitle) with a right-aligned actions area in a consistent hierarchy."
5026
+ ],
5027
+ avoidWhen: [
5028
+ "You need cross-page hierarchy or navigation trails rather than a single page title."
5029
+ ],
5030
+ alternatives: [
5031
+ {
5032
+ component: "TBreadcrumb",
5033
+ when: "You are showing where the page sits in a hierarchy, not its title."
5034
+ },
5035
+ {
5036
+ component: "TNavbar",
5037
+ when: "You need a top application bar, not a per-page title block."
5038
+ }
5039
+ ]
5040
+ },
5041
+ setup: {
5042
+ required: [],
5043
+ notes: []
5044
+ },
5045
+ recipes: []
5046
+ },
5047
+ {
5048
+ name: "TPagination",
5049
+ aliases: [],
5050
+ package: "@treeui/vue",
5051
+ kind: "primitive",
5052
+ category: "Navigation",
5053
+ status: "available",
5054
+ purpose: "Page controls with previous/next navigation, numbered page buttons, and ellipsis for large page ranges. Supports v-model for the active page.\n",
5055
+ sourceFile: "packages/vue/src/components/TPagination.vue",
5056
+ storyFile: "apps/docs/src/stories/Pagination.stories.ts",
5057
+ storyTitle: "Components/Navigation/Pagination",
5058
+ manifestFile: "docs/ai/COMPONENTS/pagination.yaml",
5059
+ props: [
5060
+ {
5061
+ name: "modelValue",
5062
+ type: "number",
5063
+ default: "1",
5064
+ description: "Current active page (1-based). Use with v-model."
5065
+ },
5066
+ {
5067
+ name: "totalPages",
5068
+ type: "number",
5069
+ required: true,
5070
+ description: "Total number of pages."
5071
+ },
5072
+ {
5073
+ name: "siblings",
5074
+ type: "number",
5075
+ default: "1",
5076
+ description: "Number of sibling page buttons shown around the current page."
5077
+ },
5078
+ {
5079
+ name: "size",
5080
+ type: "TSize",
5081
+ default: "md",
5082
+ values: [
5083
+ "sm",
5084
+ "md",
5085
+ "lg"
5086
+ ],
5087
+ description: "Size variant for the pagination controls."
5088
+ },
5089
+ {
5090
+ name: "disabled",
5091
+ type: "boolean",
5092
+ default: "false",
5093
+ description: "Disables all pagination controls."
5094
+ },
5095
+ {
5096
+ name: "label",
5097
+ type: "string",
5098
+ default: "Pagination",
5099
+ description: "Accessible label for the nav element."
5100
+ }
5101
+ ],
5102
+ events: [
5103
+ {
5104
+ name: "update:modelValue",
5105
+ description: "Emitted when the active page changes. Enables v-model binding.",
5106
+ payload: "[page: number]"
5107
+ }
5108
+ ],
5109
+ slots: [
5110
+ {
5111
+ name: "previous",
5112
+ description: "Custom content for the previous button."
5113
+ },
5114
+ {
5115
+ name: "next",
5116
+ description: "Custom content for the next button."
4637
5117
  }
4638
5118
  ],
4639
5119
  a11y: [
@@ -5221,6 +5701,20 @@ var treeui_catalog_default = {
5221
5701
  "lg"
5222
5702
  ]
5223
5703
  },
5704
+ {
5705
+ name: "width",
5706
+ type: "TFieldWidth",
5707
+ default: "full",
5708
+ values: [
5709
+ "xs",
5710
+ "sm",
5711
+ "md",
5712
+ "lg",
5713
+ "xl",
5714
+ "full"
5715
+ ],
5716
+ note: "Inline-size cap (max-inline-size). Fluid by default; use instead of an inline width style."
5717
+ },
5224
5718
  {
5225
5719
  name: "disabled",
5226
5720
  type: "boolean",
@@ -5635,6 +6129,46 @@ var treeui_catalog_default = {
5635
6129
  },
5636
6130
  recipes: []
5637
6131
  },
6132
+ {
6133
+ name: "TSpacer",
6134
+ aliases: [],
6135
+ package: "@treeui/vue",
6136
+ kind: "layout",
6137
+ category: "Layout",
6138
+ status: "available",
6139
+ purpose: "Flexible spacer that absorbs free space between siblings in a flex layout (e.g. TStack), replacing hand-written margin:auto / flex:1.",
6140
+ sourceFile: "packages/vue/src/components/TSpacer.vue",
6141
+ storyFile: "apps/docs/src/stories/Spacer.stories.ts",
6142
+ storyTitle: "Components/Layout/Spacer",
6143
+ manifestFile: "docs/ai/COMPONENTS/spacer.yaml",
6144
+ props: [
6145
+ {
6146
+ name: "as",
6147
+ type: "string",
6148
+ default: "div"
6149
+ }
6150
+ ],
6151
+ events: [],
6152
+ slots: [],
6153
+ a11y: [
6154
+ "Rendered with aria-hidden='true'; it is a layout-only element with no content."
6155
+ ],
6156
+ behavior: [
6157
+ "grows_to_fill_free_space: true",
6158
+ "pushes_siblings_apart_or_to_ends: true"
6159
+ ],
6160
+ selection: {
6161
+ keywords: [],
6162
+ useWhen: [],
6163
+ avoidWhen: [],
6164
+ alternatives: []
6165
+ },
6166
+ setup: {
6167
+ required: [],
6168
+ notes: []
6169
+ },
6170
+ recipes: []
6171
+ },
5638
6172
  {
5639
6173
  name: "TSparkline",
5640
6174
  aliases: [],
@@ -5807,6 +6341,82 @@ var treeui_catalog_default = {
5807
6341
  },
5808
6342
  recipes: []
5809
6343
  },
6344
+ {
6345
+ name: "TSplit",
6346
+ aliases: [],
6347
+ package: "@treeui/vue",
6348
+ kind: "layout",
6349
+ category: "Layout",
6350
+ status: "available",
6351
+ purpose: "Responsive two-pane layout (a wider main pane + a narrower aside) that sits side by side and stacks to one column when narrow \u2014 no media query.",
6352
+ sourceFile: "packages/vue/src/components/TSplit.vue",
6353
+ storyFile: "apps/docs/src/stories/Split.stories.ts",
6354
+ storyTitle: "Components/Layout/Split",
6355
+ manifestFile: "docs/ai/COMPONENTS/split.yaml",
6356
+ props: [
6357
+ {
6358
+ name: "as",
6359
+ type: "string",
6360
+ default: "div"
6361
+ },
6362
+ {
6363
+ name: "gap",
6364
+ type: "string",
6365
+ default: "var(--tree-space-5)"
6366
+ },
6367
+ {
6368
+ name: "ratio",
6369
+ type: "number",
6370
+ default: "2",
6371
+ note: "How many times faster the main pane grows than the aside pane."
6372
+ },
6373
+ {
6374
+ name: "minWidth",
6375
+ type: "string",
6376
+ default: "18rem",
6377
+ note: "Min width each pane keeps before the layout wraps to a single column (flex-wrap, no @media)."
6378
+ },
6379
+ {
6380
+ name: "side",
6381
+ type: "start | end",
6382
+ default: "end",
6383
+ note: "Which side the aside pane sits on when side by side."
6384
+ },
6385
+ {
6386
+ name: "align",
6387
+ type: "string",
6388
+ default: "start",
6389
+ note: "Cross-axis alignment (align-items)."
6390
+ }
6391
+ ],
6392
+ events: [],
6393
+ slots: [
6394
+ {
6395
+ name: "default"
6396
+ },
6397
+ {
6398
+ name: "aside"
6399
+ }
6400
+ ],
6401
+ a11y: [
6402
+ "Layout-only wrapper; semantics come from the slotted content."
6403
+ ],
6404
+ behavior: [
6405
+ "wraps_to_single_column_when_narrow: true",
6406
+ "media_query_free: true"
6407
+ ],
6408
+ selection: {
6409
+ keywords: [],
6410
+ useWhen: [],
6411
+ avoidWhen: [],
6412
+ alternatives: []
6413
+ },
6414
+ setup: {
6415
+ required: [],
6416
+ notes: []
6417
+ },
6418
+ recipes: []
6419
+ },
5810
6420
  {
5811
6421
  name: "TStack",
5812
6422
  aliases: [],
@@ -5858,6 +6468,12 @@ var treeui_catalog_default = {
5858
6468
  name: "reverse",
5859
6469
  type: "boolean",
5860
6470
  default: "false"
6471
+ },
6472
+ {
6473
+ name: "grow",
6474
+ type: "boolean",
6475
+ default: "false",
6476
+ note: "Expands the stack to fill available space along its parent's main axis (flex: 1 with min-size 0). Use it to make a sidebar column fill its container height."
5861
6477
  }
5862
6478
  ],
5863
6479
  events: [],
@@ -5905,6 +6521,80 @@ var treeui_catalog_default = {
5905
6521
  "chart-surface"
5906
6522
  ]
5907
6523
  },
6524
+ {
6525
+ name: "TStackItem",
6526
+ aliases: [],
6527
+ package: "@treeui/vue",
6528
+ kind: "layout",
6529
+ category: "Layout",
6530
+ status: "available",
6531
+ purpose: "Per-child flex control inside a TStack \u2014 grow, shrink, basis, min width and align-self \u2014 so one item can fill the row without hand-written CSS.",
6532
+ sourceFile: "packages/vue/src/components/TStackItem.vue",
6533
+ storyFile: "apps/docs/src/stories/StackItem.stories.ts",
6534
+ storyTitle: "Components/Layout/StackItem",
6535
+ manifestFile: "docs/ai/COMPONENTS/stack-item.yaml",
6536
+ props: [
6537
+ {
6538
+ name: "as",
6539
+ type: "string",
6540
+ default: "div"
6541
+ },
6542
+ {
6543
+ name: "grow",
6544
+ type: "boolean | number",
6545
+ default: "false",
6546
+ note: "true = flex-grow 1; a number sets the grow factor."
6547
+ },
6548
+ {
6549
+ name: "shrink",
6550
+ type: "boolean | number",
6551
+ default: "undefined",
6552
+ note: "false prevents shrinking; a number sets the shrink factor."
6553
+ },
6554
+ {
6555
+ name: "basis",
6556
+ type: "string",
6557
+ default: "undefined",
6558
+ note: "flex-basis (initial main-axis size)."
6559
+ },
6560
+ {
6561
+ name: "minWidth",
6562
+ type: "string",
6563
+ default: "undefined",
6564
+ note: "Minimum main-axis size before it wraps/shrinks."
6565
+ },
6566
+ {
6567
+ name: "align",
6568
+ type: "string",
6569
+ default: "undefined",
6570
+ note: "Per-item cross-axis alignment (align-self)."
6571
+ }
6572
+ ],
6573
+ events: [],
6574
+ slots: [
6575
+ {
6576
+ name: "default"
6577
+ }
6578
+ ],
6579
+ a11y: [
6580
+ "Layout-only wrapper; semantics come from the slotted content."
6581
+ ],
6582
+ behavior: [
6583
+ "controls_single_flex_child: true",
6584
+ "defaults_min_inline_size_zero: true"
6585
+ ],
6586
+ selection: {
6587
+ keywords: [],
6588
+ useWhen: [],
6589
+ avoidWhen: [],
6590
+ alternatives: []
6591
+ },
6592
+ setup: {
6593
+ required: [],
6594
+ notes: []
6595
+ },
6596
+ recipes: []
6597
+ },
5908
6598
  {
5909
6599
  name: "TStat",
5910
6600
  aliases: [],
@@ -6507,6 +7197,95 @@ var treeui_catalog_default = {
6507
7197
  "tag-input"
6508
7198
  ]
6509
7199
  },
7200
+ {
7201
+ name: "TText",
7202
+ aliases: [],
7203
+ package: "@treeui/vue",
7204
+ kind: "primitive",
7205
+ category: "Data Display",
7206
+ status: "available",
7207
+ purpose: "Typography primitive for body text, labels and headings \u2014 size, tone, weight and truncation from tokens, with no hand-written CSS.",
7208
+ sourceFile: "packages/vue/src/components/TText.vue",
7209
+ storyFile: "apps/docs/src/stories/Text.stories.ts",
7210
+ storyTitle: "Components/Data Display/Text",
7211
+ manifestFile: "docs/ai/COMPONENTS/text.yaml",
7212
+ props: [
7213
+ {
7214
+ name: "as",
7215
+ type: "string",
7216
+ default: "span",
7217
+ note: "Render element. Use `p`, `h1`\u2013`h6`, `label`, `small`, `strong`, \u2026 for correct semantics."
7218
+ },
7219
+ {
7220
+ name: "size",
7221
+ type: "TTextSize",
7222
+ default: "undefined",
7223
+ values: [
7224
+ "xs",
7225
+ "sm",
7226
+ "md",
7227
+ "lg",
7228
+ "xl",
7229
+ "2xl",
7230
+ "3xl"
7231
+ ],
7232
+ note: "Maps to --tree-font-size-*. Omitted = inherit."
7233
+ },
7234
+ {
7235
+ name: "tone",
7236
+ type: "TTextTone",
7237
+ default: "default",
7238
+ values: [
7239
+ "default",
7240
+ "muted",
7241
+ "inverse",
7242
+ "brand"
7243
+ ],
7244
+ note: "`default` inherits the current colour; others map to --tree-color-text-* / brand."
7245
+ },
7246
+ {
7247
+ name: "weight",
7248
+ type: "TTextWeight",
7249
+ default: "undefined",
7250
+ values: [
7251
+ "regular",
7252
+ "medium",
7253
+ "semibold",
7254
+ "bold"
7255
+ ]
7256
+ },
7257
+ {
7258
+ name: "truncate",
7259
+ type: "boolean",
7260
+ default: "false",
7261
+ note: "Clip to a single line with an ellipsis."
7262
+ }
7263
+ ],
7264
+ events: [],
7265
+ slots: [
7266
+ {
7267
+ name: "default"
7268
+ }
7269
+ ],
7270
+ a11y: [
7271
+ "Choose `as` to convey the correct semantics (e.g. headings via h1\u2013h6); TText adds only visual styling."
7272
+ ],
7273
+ behavior: [
7274
+ "semantic_element_controlled_by_as: true",
7275
+ "token_driven_size_tone_weight: true"
7276
+ ],
7277
+ selection: {
7278
+ keywords: [],
7279
+ useWhen: [],
7280
+ avoidWhen: [],
7281
+ alternatives: []
7282
+ },
7283
+ setup: {
7284
+ required: [],
7285
+ notes: []
7286
+ },
7287
+ recipes: []
7288
+ },
6510
7289
  {
6511
7290
  name: "TTextarea",
6512
7291
  aliases: [],
@@ -6535,6 +7314,20 @@ var treeui_catalog_default = {
6535
7314
  "lg"
6536
7315
  ]
6537
7316
  },
7317
+ {
7318
+ name: "width",
7319
+ type: "TFieldWidth",
7320
+ default: "full",
7321
+ values: [
7322
+ "xs",
7323
+ "sm",
7324
+ "md",
7325
+ "lg",
7326
+ "xl",
7327
+ "full"
7328
+ ],
7329
+ note: "Inline-size cap (max-inline-size). Fluid by default; use instead of an inline width style."
7330
+ },
6538
7331
  {
6539
7332
  name: "disabled",
6540
7333
  type: "boolean",