#
tokens: 44862/50000 2/1633 files (page 97/143)
lines: off (toggle) GitHub
raw markdown copy
This is page 97 of 143. Use http://codebase.md/xmlui-org/xmlui/tools/vscode/resources/assets/img/bg-iphone-14-pro.jpg?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   └── config.json
├── .eslintrc.cjs
├── .github
│   ├── build-checklist.png
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── deploy-blog-optimized.yml
│       ├── deploy-blog-swa.yml
│       ├── deploy-blog.yml
│       ├── deploy-docs-optimized.yml
│       ├── deploy-docs.yml
│       ├── prepare-versions.yml
│       ├── release-packages.yml
│       ├── run-all-tests.yml
│       └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│   ├── launch.json
│   └── settings.json
├── blog
│   ├── .gitignore
│   ├── .gitkeep
│   ├── CHANGELOG.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── blog
│   │   │   ├── images
│   │   │   │   ├── an-advanced-codefence.gif
│   │   │   │   ├── an-advanced-codefence.mp4
│   │   │   │   ├── blog-page-component.png
│   │   │   │   ├── blog-scrabble.png
│   │   │   │   ├── codefence-runner.png
│   │   │   │   ├── integrated-blog-search.png
│   │   │   │   ├── lorem-ipsum.png
│   │   │   │   ├── playground-checkbox-source.png
│   │   │   │   ├── playground.png
│   │   │   │   ├── use-xmlui-mcp-to-find-a-howto.png
│   │   │   │   └── xmlui-demo-gallery.png
│   │   │   ├── introducing-xmlui.md
│   │   │   ├── lorem-ipsum.md
│   │   │   ├── newest-post.md
│   │   │   ├── older-post.md
│   │   │   ├── xmlui-playground.md
│   │   │   └── xmlui-powered-blog.md
│   │   ├── mockServiceWorker.js
│   │   ├── resources
│   │   │   ├── favicon.ico
│   │   │   ├── files
│   │   │   │   └── for-download
│   │   │   │       └── xmlui
│   │   │   │           └── xmlui-standalone.umd.js
│   │   │   ├── github.svg
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   ├── rss.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   ├── staticwebapp.config.json
│   │   └── web.config
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   └── PageNotFound.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       └── blog-theme.ts
│   └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│   ├── .gitignore
│   ├── CHANGELOG.md
│   ├── ComponentRefLinks.txt
│   ├── content
│   │   ├── _meta.json
│   │   ├── components
│   │   │   ├── _meta.json
│   │   │   ├── _overview.md
│   │   │   ├── APICall.md
│   │   │   ├── App.md
│   │   │   ├── AppHeader.md
│   │   │   ├── AppState.md
│   │   │   ├── AutoComplete.md
│   │   │   ├── Avatar.md
│   │   │   ├── Backdrop.md
│   │   │   ├── Badge.md
│   │   │   ├── BarChart.md
│   │   │   ├── Bookmark.md
│   │   │   ├── Breakout.md
│   │   │   ├── Button.md
│   │   │   ├── Card.md
│   │   │   ├── Carousel.md
│   │   │   ├── ChangeListener.md
│   │   │   ├── Checkbox.md
│   │   │   ├── CHStack.md
│   │   │   ├── ColorPicker.md
│   │   │   ├── Column.md
│   │   │   ├── ContentSeparator.md
│   │   │   ├── CVStack.md
│   │   │   ├── DataSource.md
│   │   │   ├── DateInput.md
│   │   │   ├── DatePicker.md
│   │   │   ├── DonutChart.md
│   │   │   ├── DropdownMenu.md
│   │   │   ├── EmojiSelector.md
│   │   │   ├── ExpandableItem.md
│   │   │   ├── FileInput.md
│   │   │   ├── FileUploadDropZone.md
│   │   │   ├── FlowLayout.md
│   │   │   ├── Footer.md
│   │   │   ├── Form.md
│   │   │   ├── FormItem.md
│   │   │   ├── FormSection.md
│   │   │   ├── Fragment.md
│   │   │   ├── H1.md
│   │   │   ├── H2.md
│   │   │   ├── H3.md
│   │   │   ├── H4.md
│   │   │   ├── H5.md
│   │   │   ├── H6.md
│   │   │   ├── Heading.md
│   │   │   ├── HSplitter.md
│   │   │   ├── HStack.md
│   │   │   ├── Icon.md
│   │   │   ├── IFrame.md
│   │   │   ├── Image.md
│   │   │   ├── Items.md
│   │   │   ├── LabelList.md
│   │   │   ├── Legend.md
│   │   │   ├── LineChart.md
│   │   │   ├── Link.md
│   │   │   ├── List.md
│   │   │   ├── Logo.md
│   │   │   ├── Markdown.md
│   │   │   ├── MenuItem.md
│   │   │   ├── MenuSeparator.md
│   │   │   ├── ModalDialog.md
│   │   │   ├── NavGroup.md
│   │   │   ├── NavLink.md
│   │   │   ├── NavPanel.md
│   │   │   ├── NoResult.md
│   │   │   ├── NumberBox.md
│   │   │   ├── Option.md
│   │   │   ├── Page.md
│   │   │   ├── PageMetaTitle.md
│   │   │   ├── Pages.md
│   │   │   ├── Pagination.md
│   │   │   ├── PasswordInput.md
│   │   │   ├── PieChart.md
│   │   │   ├── ProgressBar.md
│   │   │   ├── Queue.md
│   │   │   ├── RadioGroup.md
│   │   │   ├── RealTimeAdapter.md
│   │   │   ├── Redirect.md
│   │   │   ├── Select.md
│   │   │   ├── Slider.md
│   │   │   ├── Slot.md
│   │   │   ├── SpaceFiller.md
│   │   │   ├── Spinner.md
│   │   │   ├── Splitter.md
│   │   │   ├── Stack.md
│   │   │   ├── StickyBox.md
│   │   │   ├── SubMenuItem.md
│   │   │   ├── Switch.md
│   │   │   ├── TabItem.md
│   │   │   ├── Table.md
│   │   │   ├── TableOfContents.md
│   │   │   ├── Tabs.md
│   │   │   ├── Text.md
│   │   │   ├── TextArea.md
│   │   │   ├── TextBox.md
│   │   │   ├── Theme.md
│   │   │   ├── TimeInput.md
│   │   │   ├── Timer.md
│   │   │   ├── ToneChangerButton.md
│   │   │   ├── ToneSwitch.md
│   │   │   ├── Tooltip.md
│   │   │   ├── Tree.md
│   │   │   ├── VSplitter.md
│   │   │   ├── VStack.md
│   │   │   ├── xmlui-animations
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── Animation.md
│   │   │   │   ├── FadeAnimation.md
│   │   │   │   ├── FadeInAnimation.md
│   │   │   │   ├── FadeOutAnimation.md
│   │   │   │   ├── ScaleAnimation.md
│   │   │   │   └── SlideInAnimation.md
│   │   │   ├── xmlui-pdf
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Pdf.md
│   │   │   ├── xmlui-spreadsheet
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Spreadsheet.md
│   │   │   └── xmlui-website-blocks
│   │   │       ├── _meta.json
│   │   │       ├── _overview.md
│   │   │       ├── Carousel.md
│   │   │       ├── HelloMd.md
│   │   │       ├── HeroSection.md
│   │   │       └── ScrollToTop.md
│   │   └── extensions
│   │       ├── _meta.json
│   │       ├── xmlui-animations
│   │       │   ├── _meta.json
│   │       │   ├── _overview.md
│   │       │   ├── Animation.md
│   │       │   ├── FadeAnimation.md
│   │       │   ├── FadeInAnimation.md
│   │       │   ├── FadeOutAnimation.md
│   │       │   ├── ScaleAnimation.md
│   │       │   └── SlideInAnimation.md
│   │       └── xmlui-website-blocks
│   │           ├── _meta.json
│   │           ├── _overview.md
│   │           ├── Carousel.md
│   │           ├── FancyButton.md
│   │           ├── HeroSection.md
│   │           └── ScrollToTop.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── feed.rss
│   │   ├── mockServiceWorker.js
│   │   ├── pages
│   │   │   ├── _meta.json
│   │   │   ├── app-structure.md
│   │   │   ├── build-editor-component.md
│   │   │   ├── build-hello-world-component.md
│   │   │   ├── components-intro.md
│   │   │   ├── context-variables.md
│   │   │   ├── forms.md
│   │   │   ├── globals.md
│   │   │   ├── glossary.md
│   │   │   ├── helper-tags.md
│   │   │   ├── hosted-deployment.md
│   │   │   ├── howto
│   │   │   │   ├── assign-a-complex-json-literal-to-a-component-variable.md
│   │   │   │   ├── chain-a-refetch.md
│   │   │   │   ├── control-cache-invalidation.md
│   │   │   │   ├── debounce-user-input-for-api-calls.md
│   │   │   │   ├── debounce-with-changelistener.md
│   │   │   │   ├── debug-a-component.md
│   │   │   │   ├── delay-a-datasource-until-another-datasource-is-ready.md
│   │   │   │   ├── delegate-a-method.md
│   │   │   │   ├── do-custom-form-validation.md
│   │   │   │   ├── expose-a-method-from-a-component.md
│   │   │   │   ├── filter-and-transform-data-from-an-api.md
│   │   │   │   ├── group-items-in-list-by-a-property.md
│   │   │   │   ├── handle-background-operations.md
│   │   │   │   ├── hide-an-element-until-its-datasource-is-ready.md
│   │   │   │   ├── make-a-set-of-equal-width-cards.md
│   │   │   │   ├── make-a-table-responsive.md
│   │   │   │   ├── make-navpanel-width-responsive.md
│   │   │   │   ├── modify-a-value-reported-in-a-column.md
│   │   │   │   ├── paginate-a-list.md
│   │   │   │   ├── pass-data-to-a-modal-dialog.md
│   │   │   │   ├── react-to-button-click-not-keystrokes.md
│   │   │   │   ├── set-the-initial-value-of-a-select-from-fetched-data.md
│   │   │   │   ├── share-a-modaldialog-across-components.md
│   │   │   │   ├── sync-selections-between-table-and-list-views.md
│   │   │   │   ├── update-ui-optimistically.md
│   │   │   │   ├── use-built-in-form-validation.md
│   │   │   │   └── use-the-same-modaldialog-to-add-or-edit.md
│   │   │   ├── howto.md
│   │   │   ├── intro.md
│   │   │   ├── layout.md
│   │   │   ├── markup.md
│   │   │   ├── mcp.md
│   │   │   ├── modal-dialogs.md
│   │   │   ├── news-and-reviews.md
│   │   │   ├── reactive-intro.md
│   │   │   ├── refactoring.md
│   │   │   ├── routing-and-links.md
│   │   │   ├── samples
│   │   │   │   ├── color-palette.xmlui
│   │   │   │   ├── color-values.xmlui
│   │   │   │   ├── shadow-sizes.xmlui
│   │   │   │   ├── spacing-sizes.xmlui
│   │   │   │   ├── swatch.xmlui
│   │   │   │   ├── theme-gallery-brief.xmlui
│   │   │   │   └── theme-gallery.xmlui
│   │   │   ├── scoping.md
│   │   │   ├── scripting.md
│   │   │   ├── styles-and-themes
│   │   │   │   ├── common-units.md
│   │   │   │   ├── layout-props.md
│   │   │   │   ├── theme-variable-defaults.md
│   │   │   │   ├── theme-variables.md
│   │   │   │   └── themes.md
│   │   │   ├── template-properties.md
│   │   │   ├── test.md
│   │   │   ├── tutorial-01.md
│   │   │   ├── tutorial-02.md
│   │   │   ├── tutorial-03.md
│   │   │   ├── tutorial-04.md
│   │   │   ├── tutorial-05.md
│   │   │   ├── tutorial-06.md
│   │   │   ├── tutorial-07.md
│   │   │   ├── tutorial-08.md
│   │   │   ├── tutorial-09.md
│   │   │   ├── tutorial-10.md
│   │   │   ├── tutorial-11.md
│   │   │   ├── tutorial-12.md
│   │   │   ├── universal-properties.md
│   │   │   ├── user-defined-components.md
│   │   │   ├── vscode.md
│   │   │   ├── working-with-markdown.md
│   │   │   ├── working-with-text.md
│   │   │   ├── xmlui-animations
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── Animation.md
│   │   │   │   ├── FadeAnimation.md
│   │   │   │   ├── FadeInAnimation.md
│   │   │   │   ├── FadeOutAnimation.md
│   │   │   │   ├── ScaleAnimation.md
│   │   │   │   └── SlideInAnimation.md
│   │   │   ├── xmlui-charts
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── BarChart.md
│   │   │   │   ├── DonutChart.md
│   │   │   │   ├── LabelList.md
│   │   │   │   ├── Legend.md
│   │   │   │   ├── LineChart.md
│   │   │   │   └── PieChart.md
│   │   │   ├── xmlui-pdf
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Pdf.md
│   │   │   └── xmlui-spreadsheet
│   │   │       ├── _meta.json
│   │   │       ├── _overview.md
│   │   │       └── Spreadsheet.md
│   │   ├── resources
│   │   │   ├── devdocs
│   │   │   │   ├── debug-proxy-object-2.png
│   │   │   │   ├── debug-proxy-object.png
│   │   │   │   ├── table_editor_01.png
│   │   │   │   ├── table_editor_02.png
│   │   │   │   ├── table_editor_03.png
│   │   │   │   ├── table_editor_04.png
│   │   │   │   ├── table_editor_05.png
│   │   │   │   ├── table_editor_06.png
│   │   │   │   ├── table_editor_07.png
│   │   │   │   ├── table_editor_08.png
│   │   │   │   ├── table_editor_09.png
│   │   │   │   ├── table_editor_10.png
│   │   │   │   ├── table_editor_11.png
│   │   │   │   ├── table-editor-01.png
│   │   │   │   ├── table-editor-02.png
│   │   │   │   ├── table-editor-03.png
│   │   │   │   ├── table-editor-04.png
│   │   │   │   ├── table-editor-06.png
│   │   │   │   ├── table-editor-07.png
│   │   │   │   ├── table-editor-08.png
│   │   │   │   ├── table-editor-09.png
│   │   │   │   └── xmlui-rendering-of-tiptap-markdown.png
│   │   │   ├── favicon.ico
│   │   │   ├── files
│   │   │   │   ├── clients.json
│   │   │   │   ├── daily-revenue.json
│   │   │   │   ├── dashboard-stats.json
│   │   │   │   ├── demo.xmlui
│   │   │   │   ├── demo.xmlui.xs
│   │   │   │   ├── downloads
│   │   │   │   │   └── downloads.json
│   │   │   │   ├── for-download
│   │   │   │   │   ├── index-with-api.html
│   │   │   │   │   ├── index.html
│   │   │   │   │   ├── mockApi.js
│   │   │   │   │   ├── start-darwin.sh
│   │   │   │   │   ├── start-linux.sh
│   │   │   │   │   ├── start.bat
│   │   │   │   │   └── xmlui
│   │   │   │   │       └── xmlui-standalone.umd.js
│   │   │   │   ├── getting-started
│   │   │   │   │   ├── cl-tutorial-final.zip
│   │   │   │   │   ├── cl-tutorial.zip
│   │   │   │   │   ├── cl-tutorial2.zip
│   │   │   │   │   ├── cl-tutorial3.zip
│   │   │   │   │   ├── cl-tutorial4.zip
│   │   │   │   │   ├── cl-tutorial5.zip
│   │   │   │   │   ├── cl-tutorial6.zip
│   │   │   │   │   ├── getting-started.zip
│   │   │   │   │   ├── hello-xmlui.zip
│   │   │   │   │   ├── xmlui-empty.zip
│   │   │   │   │   └── xmlui-starter.zip
│   │   │   │   ├── howto
│   │   │   │   │   └── component-icons
│   │   │   │   │       └── up-arrow.svg
│   │   │   │   ├── invoices.json
│   │   │   │   ├── monthly-status.json
│   │   │   │   ├── news-and-reviews.json
│   │   │   │   ├── products.json
│   │   │   │   ├── releases.json
│   │   │   │   ├── tutorials
│   │   │   │   │   ├── datasource
│   │   │   │   │   │   └── api.ts
│   │   │   │   │   └── p2do
│   │   │   │   │       ├── api.ts
│   │   │   │   │       └── todo-logo.svg
│   │   │   │   └── xmlui.json
│   │   │   ├── github.svg
│   │   │   ├── images
│   │   │   │   ├── apiaction-tutorial
│   │   │   │   │   ├── add-success.png
│   │   │   │   │   ├── apiaction-param.png
│   │   │   │   │   ├── change-completed.png
│   │   │   │   │   ├── change-in-progress.png
│   │   │   │   │   ├── confirm-delete.png
│   │   │   │   │   ├── data-error.png
│   │   │   │   │   ├── data-progress.png
│   │   │   │   │   ├── data-success.png
│   │   │   │   │   ├── display-1.png
│   │   │   │   │   ├── item-deleted.png
│   │   │   │   │   ├── item-updated.png
│   │   │   │   │   ├── missing-api-key.png
│   │   │   │   │   ├── new-item-added.png
│   │   │   │   │   └── test-message.png
│   │   │   │   ├── chat-api
│   │   │   │   │   └── domain-model.svg
│   │   │   │   ├── components
│   │   │   │   │   ├── image
│   │   │   │   │   │   └── breakfast.jpg
│   │   │   │   │   ├── markdown
│   │   │   │   │   │   └── colors.png
│   │   │   │   │   └── modal
│   │   │   │   │       ├── deep_link_dialog_1.jpg
│   │   │   │   │       └── deep_link_dialog_2.jpg
│   │   │   │   ├── create-apps
│   │   │   │   │   ├── collapsed-vertical.png
│   │   │   │   │   ├── using-forms-warning-dialog.png
│   │   │   │   │   └── using-forms.png
│   │   │   │   ├── datasource-tutorial
│   │   │   │   │   ├── data-with-header.png
│   │   │   │   │   ├── filtered-data.png
│   │   │   │   │   ├── filtered-items.png
│   │   │   │   │   ├── initial-page-items.png
│   │   │   │   │   ├── list-items.png
│   │   │   │   │   ├── next-page-items.png
│   │   │   │   │   ├── no-data.png
│   │   │   │   │   ├── pagination-1.jpg
│   │   │   │   │   ├── pagination-1.png
│   │   │   │   │   ├── polling-1.png
│   │   │   │   │   ├── refetch-data.png
│   │   │   │   │   ├── slow-loading.png
│   │   │   │   │   ├── test-message.png
│   │   │   │   │   ├── Thumbs.db
│   │   │   │   │   ├── unconventional-data.png
│   │   │   │   │   └── unfiltered-items.png
│   │   │   │   ├── flower.jpg
│   │   │   │   ├── get-started
│   │   │   │   │   ├── add-new-contact.png
│   │   │   │   │   ├── app-modified.png
│   │   │   │   │   ├── app-start.png
│   │   │   │   │   ├── app-with-boxes.png
│   │   │   │   │   ├── app-with-toast.png
│   │   │   │   │   ├── boilerplate-structure.png
│   │   │   │   │   ├── cl-initial.png
│   │   │   │   │   ├── cl-start.png
│   │   │   │   │   ├── contact-counts.png
│   │   │   │   │   ├── contact-dialog-title.png
│   │   │   │   │   ├── contact-dialog.png
│   │   │   │   │   ├── contact-menus.png
│   │   │   │   │   ├── contact-predicates.png
│   │   │   │   │   ├── context-menu.png
│   │   │   │   │   ├── dashboard-numbers.png
│   │   │   │   │   ├── default-contact-list.png
│   │   │   │   │   ├── delete-contact.png
│   │   │   │   │   ├── delete-task.png
│   │   │   │   │   ├── detailed-template.png
│   │   │   │   │   ├── edit-contact-details.png
│   │   │   │   │   ├── edited-contact-saved.png
│   │   │   │   │   ├── empty-sections.png
│   │   │   │   │   ├── filter-completed.png
│   │   │   │   │   ├── fullwidth-desktop.png
│   │   │   │   │   ├── fullwidth-mobile.png
│   │   │   │   │   ├── initial-table.png
│   │   │   │   │   ├── items-and-badges.png
│   │   │   │   │   ├── loading-message.png
│   │   │   │   │   ├── new-contact-button.png
│   │   │   │   │   ├── new-contact-saved.png
│   │   │   │   │   ├── no-empty-sections.png
│   │   │   │   │   ├── personal-todo-initial.png
│   │   │   │   │   ├── piechart.png
│   │   │   │   │   ├── review-today.png
│   │   │   │   │   ├── rudimentary-dashboard.png
│   │   │   │   │   ├── section-collapsed.png
│   │   │   │   │   ├── sectioned-items.png
│   │   │   │   │   ├── sections-ordered.png
│   │   │   │   │   ├── spacex-list-with-links.png
│   │   │   │   │   ├── spacex-list.png
│   │   │   │   │   ├── start-personal-todo-1.png
│   │   │   │   │   ├── submit-new-contact.png
│   │   │   │   │   ├── submit-new-task.png
│   │   │   │   │   ├── syntax-highlighting.png
│   │   │   │   │   ├── table-with-badge.png
│   │   │   │   │   ├── template-with-card.png
│   │   │   │   │   ├── test-emulated-api.png
│   │   │   │   │   ├── Thumbs.db
│   │   │   │   │   ├── todo-logo.png
│   │   │   │   │   └── xmlui-tools.png
│   │   │   │   ├── HelloApp.png
│   │   │   │   ├── HelloApp2.png
│   │   │   │   ├── logos
│   │   │   │   │   ├── xmlui1.svg
│   │   │   │   │   ├── xmlui2.svg
│   │   │   │   │   ├── xmlui3.svg
│   │   │   │   │   ├── xmlui4.svg
│   │   │   │   │   ├── xmlui5.svg
│   │   │   │   │   ├── xmlui6.svg
│   │   │   │   │   └── xmlui7.svg
│   │   │   │   ├── pdf
│   │   │   │   │   └── dummy-pdf.jpg
│   │   │   │   ├── rendering-engine
│   │   │   │   │   ├── AppEngine-flow.svg
│   │   │   │   │   ├── Component.svg
│   │   │   │   │   ├── CompoundComponent.svg
│   │   │   │   │   ├── RootComponent.svg
│   │   │   │   │   └── tree-with-containers.svg
│   │   │   │   ├── reviewers-guide
│   │   │   │   │   ├── AppEngine-flow.svg
│   │   │   │   │   └── incbutton-in-action.png
│   │   │   │   ├── tools
│   │   │   │   │   └── boilerplate-structure.png
│   │   │   │   ├── try.svg
│   │   │   │   ├── tutorial
│   │   │   │   │   ├── app-chat-history.png
│   │   │   │   │   ├── app-content-placeholder.png
│   │   │   │   │   ├── app-header-and-content.png
│   │   │   │   │   ├── app-links-channel-selected.png
│   │   │   │   │   ├── app-links-click.png
│   │   │   │   │   ├── app-navigation.png
│   │   │   │   │   ├── finished-ex01.png
│   │   │   │   │   ├── finished-ex02.png
│   │   │   │   │   ├── hello.png
│   │   │   │   │   ├── splash-screen-advanced.png
│   │   │   │   │   ├── splash-screen-after-click.png
│   │   │   │   │   ├── splash-screen-centered.png
│   │   │   │   │   ├── splash-screen-events.png
│   │   │   │   │   ├── splash-screen-expression.png
│   │   │   │   │   ├── splash-screen-reuse-after.png
│   │   │   │   │   ├── splash-screen-reuse-before.png
│   │   │   │   │   └── splash-screen.png
│   │   │   │   └── tutorial-01.png
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   └── web.config
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   ├── Boxes.xmlui
│   │   │   ├── Breadcrumb.xmlui
│   │   │   ├── ChangeLog.xmlui
│   │   │   ├── ColorPalette.xmlui
│   │   │   ├── DocumentLinks.xmlui
│   │   │   ├── DocumentPage.xmlui
│   │   │   ├── DocumentPageNoTOC.xmlui
│   │   │   ├── Icons.xmlui
│   │   │   ├── IncButton.xmlui
│   │   │   ├── IncButton2.xmlui
│   │   │   ├── NameValue.xmlui
│   │   │   ├── PageNotFound.xmlui
│   │   │   ├── PaletteItem.xmlui
│   │   │   ├── Palettes.xmlui
│   │   │   ├── SectionHeader.xmlui
│   │   │   ├── TBD.xmlui
│   │   │   ├── Test.xmlui
│   │   │   ├── ThemesIntro.xmlui
│   │   │   ├── ThousandThemes.xmlui
│   │   │   ├── TubeStops.xmlui
│   │   │   ├── TubeStops.xmlui.xs
│   │   │   └── TwoColumnCode.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       ├── docs-theme.ts
│   │       ├── earthtone.ts
│   │       ├── xmlui-gray-on-default.ts
│   │       ├── xmlui-green-on-default.ts
│   │       └── xmlui-orange-on-default.ts
│   └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│   ├── tsconfig.json
│   ├── xmlui-animations
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── Animation.tsx
│   │       ├── AnimationNative.tsx
│   │       ├── FadeAnimation.tsx
│   │       ├── FadeInAnimation.tsx
│   │       ├── FadeOutAnimation.tsx
│   │       ├── index.tsx
│   │       ├── ScaleAnimation.tsx
│   │       └── SlideInAnimation.tsx
│   ├── xmlui-devtools
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── devtools
│   │   │   │   ├── DevTools.tsx
│   │   │   │   ├── DevToolsNative.module.scss
│   │   │   │   ├── DevToolsNative.tsx
│   │   │   │   ├── ModalDialog.module.scss
│   │   │   │   ├── ModalDialog.tsx
│   │   │   │   ├── ModalVisibilityContext.tsx
│   │   │   │   ├── Tooltip.module.scss
│   │   │   │   ├── Tooltip.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── editor
│   │   │   │   └── Editor.tsx
│   │   │   └── index.tsx
│   │   └── vite.config-overrides.ts
│   ├── xmlui-hello-world
│   │   ├── .gitignore
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── HelloWorld.module.scss
│   │       ├── HelloWorld.tsx
│   │       ├── HelloWorldNative.tsx
│   │       └── index.tsx
│   ├── xmlui-os-frames
│   │   ├── .gitignore
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── IPhoneFrame.module.scss
│   │       ├── IPhoneFrame.tsx
│   │       ├── MacOSAppFrame.module.scss
│   │       ├── MacOSAppFrame.tsx
│   │       ├── WindowsAppFrame.module.scss
│   │       └── WindowsAppFrame.tsx
│   ├── xmlui-pdf
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   ├── components
│   │   │   │   └── Pdf.xmlui
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── LazyPdfNative.tsx
│   │       ├── Pdf.module.scss
│   │       └── Pdf.tsx
│   ├── xmlui-playground
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── hooks
│   │       │   ├── usePlayground.ts
│   │       │   └── useToast.ts
│   │       ├── index.tsx
│   │       ├── playground
│   │       │   ├── Box.module.scss
│   │       │   ├── Box.tsx
│   │       │   ├── CodeSelector.tsx
│   │       │   ├── ConfirmationDialog.module.scss
│   │       │   ├── ConfirmationDialog.tsx
│   │       │   ├── Editor.tsx
│   │       │   ├── Header.module.scss
│   │       │   ├── Header.tsx
│   │       │   ├── Playground.tsx
│   │       │   ├── PlaygroundContent.module.scss
│   │       │   ├── PlaygroundContent.tsx
│   │       │   ├── PlaygroundNative.module.scss
│   │       │   ├── PlaygroundNative.tsx
│   │       │   ├── Preview.module.scss
│   │       │   ├── Preview.tsx
│   │       │   ├── Select.module.scss
│   │       │   ├── StandalonePlayground.tsx
│   │       │   ├── StandalonePlaygroundNative.module.scss
│   │       │   ├── StandalonePlaygroundNative.tsx
│   │       │   ├── ThemeSwitcher.module.scss
│   │       │   ├── ThemeSwitcher.tsx
│   │       │   ├── ToneSwitcher.tsx
│   │       │   ├── Tooltip.module.scss
│   │       │   ├── Tooltip.tsx
│   │       │   └── utils.ts
│   │       ├── providers
│   │       │   ├── Toast.module.scss
│   │       │   └── ToastProvider.tsx
│   │       ├── state
│   │       │   └── store.ts
│   │       ├── themes
│   │       │   └── theme.ts
│   │       └── utils
│   │           └── helpers.ts
│   ├── xmlui-search
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── Search.module.scss
│   │       └── Search.tsx
│   ├── xmlui-spreadsheet
│   │   ├── .gitignore
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── Spreadsheet.tsx
│   │       └── SpreadsheetNative.tsx
│   └── xmlui-website-blocks
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── demo
│       │   ├── components
│       │   │   ├── HeroBackgroundBreakoutPage.xmlui
│       │   │   ├── HeroBackgroundsPage.xmlui
│       │   │   ├── HeroContentsPage.xmlui
│       │   │   ├── HeroTextAlignPage.xmlui
│       │   │   ├── HeroTextPage.xmlui
│       │   │   └── HeroTonesPage.xmlui
│       │   ├── Main.xmlui
│       │   └── themes
│       │       └── default.ts
│       ├── index.html
│       ├── index.ts
│       ├── meta
│       │   └── componentsMetadata.ts
│       ├── package.json
│       ├── public
│       │   └── resources
│       │       ├── building.jpg
│       │       └── xmlui-logo.svg
│       └── src
│           ├── Carousel
│           │   ├── Carousel.module.scss
│           │   ├── Carousel.tsx
│           │   ├── CarouselContext.tsx
│           │   └── CarouselNative.tsx
│           ├── FancyButton
│           │   ├── FancyButton.module.scss
│           │   ├── FancyButton.tsx
│           │   └── FancyButton.xmlui
│           ├── Hello
│           │   ├── Hello.tsx
│           │   ├── Hello.xmlui
│           │   └── Hello.xmlui.xs
│           ├── HeroSection
│           │   ├── HeroSection.module.scss
│           │   ├── HeroSection.spec.ts
│           │   ├── HeroSection.tsx
│           │   └── HeroSectionNative.tsx
│           ├── index.tsx
│           ├── ScrollToTop
│           │   ├── ScrollToTop.module.scss
│           │   ├── ScrollToTop.tsx
│           │   └── ScrollToTopNative.tsx
│           └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│   ├── codefence
│   │   └── xmlui-code-fence-docs.md
│   ├── create-app
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── create-app.ts
│   │   ├── helpers
│   │   │   ├── copy.ts
│   │   │   ├── get-pkg-manager.ts
│   │   │   ├── git.ts
│   │   │   ├── install.ts
│   │   │   ├── is-folder-empty.ts
│   │   │   ├── is-writeable.ts
│   │   │   ├── make-dir.ts
│   │   │   └── validate-pkg.ts
│   │   ├── index.ts
│   │   ├── package.json
│   │   ├── templates
│   │   │   ├── default
│   │   │   │   └── ts
│   │   │   │       ├── gitignore
│   │   │   │       ├── index.html
│   │   │   │       ├── index.ts
│   │   │   │       ├── public
│   │   │   │       │   ├── mockServiceWorker.js
│   │   │   │       │   ├── resources
│   │   │   │       │   │   ├── favicon.ico
│   │   │   │       │   │   └── xmlui-logo.svg
│   │   │   │       │   └── serve.json
│   │   │   │       └── src
│   │   │   │           ├── components
│   │   │   │           │   ├── ApiAware.xmlui
│   │   │   │           │   ├── Home.xmlui
│   │   │   │           │   ├── IncButton.xmlui
│   │   │   │           │   └── PagePanel.xmlui
│   │   │   │           ├── config.ts
│   │   │   │           └── Main.xmlui
│   │   │   ├── index.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── create-xmlui-hello-world
│   │   ├── index.js
│   │   └── package.json
│   └── vscode
│       ├── .gitignore
│       ├── .vscode
│       │   ├── launch.json
│       │   └── tasks.json
│       ├── .vscodeignore
│       ├── build.sh
│       ├── CHANGELOG.md
│       ├── esbuild.js
│       ├── eslint.config.mjs
│       ├── formatter-docs.md
│       ├── generate-test-sample.sh
│       ├── LICENSE.md
│       ├── package-lock.json
│       ├── package.json
│       ├── README.md
│       ├── resources
│       │   ├── xmlui-logo.png
│       │   └── xmlui-markup-syntax-highlighting.png
│       ├── src
│       │   ├── extension.ts
│       │   └── server.ts
│       ├── syntaxes
│       │   └── xmlui.tmLanguage.json
│       ├── test-samples
│       │   └── sample.xmlui
│       ├── tsconfig.json
│       └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
    ├── .gitignore
    ├── bin
    │   ├── bootstrap.cjs
    │   ├── bootstrap.js
    │   ├── build-lib.ts
    │   ├── build.ts
    │   ├── index.ts
    │   ├── preview.ts
    │   ├── start.ts
    │   ├── vite-xmlui-plugin.ts
    │   └── viteConfig.ts
    ├── CHANGELOG.md
    ├── conventions
    │   ├── component-qa-checklist.md
    │   ├── copilot-conventions.md
    │   ├── create-xmlui-components.md
    │   ├── mermaid.md
    │   ├── testing-conventions.md
    │   └── xmlui-in-a-nutshell.md
    ├── dev-docs
    │   ├── accessibility.md
    │   ├── build-system.md
    │   ├── build-xmlui.md
    │   ├── component-behaviors.md
    │   ├── component-metadata.md
    │   ├── components-with-options.md
    │   ├── containers.md
    │   ├── data-operations.md
    │   ├── glossary.md
    │   ├── index.md
    │   ├── next
    │   │   ├── component-dev-guide.md
    │   │   ├── configuration-management-enhancement-summary.md
    │   │   ├── documentation-scripts-refactoring-complete-summary.md
    │   │   ├── documentation-scripts-refactoring-plan.md
    │   │   ├── duplicate-pattern-extraction-summary.md
    │   │   ├── error-handling-standardization-summary.md
    │   │   ├── generating-component-reference.md
    │   │   ├── index.md
    │   │   ├── logging-consistency-implementation-summary.md
    │   │   ├── project-build.md
    │   │   ├── project-structure.md
    │   │   ├── theme-context.md
    │   │   ├── tiptap-design-considerations.md
    │   │   ├── working-with-code.md
    │   │   ├── xmlui-runtime-architecture
    │   │   └── xmlui-wcag-accessibility-report.md
    │   ├── react-fundamentals.md
    │   ├── release-method.md
    │   ├── standalone-app.md
    │   ├── theme-variables-refactoring.md
    │   ├── ud-components.md
    │   └── xmlui-repo.md
    ├── package.json
    ├── scripts
    │   ├── coverage-only.js
    │   ├── e2e-test-summary.js
    │   ├── extract-component-metadata.js
    │   ├── generate-docs
    │   │   ├── build-downloads-map.mjs
    │   │   ├── build-pages-map.mjs
    │   │   ├── components-config.json
    │   │   ├── configuration-management.mjs
    │   │   ├── constants.mjs
    │   │   ├── create-theme-files.mjs
    │   │   ├── DocsGenerator.mjs
    │   │   ├── error-handling.mjs
    │   │   ├── extensions-config.json
    │   │   ├── folders.mjs
    │   │   ├── generate-summary-files.mjs
    │   │   ├── get-docs.mjs
    │   │   ├── input-handler.mjs
    │   │   ├── logger.mjs
    │   │   ├── logging-standards.mjs
    │   │   ├── MetadataProcessor.mjs
    │   │   ├── pattern-utilities.mjs
    │   │   └── utils.mjs
    │   ├── generate-metadata-markdown.js
    │   ├── get-langserver-metadata.js
    │   ├── inline-links.mjs
    │   └── README-e2e-summary.md
    ├── src
    │   ├── abstractions
    │   │   ├── _conventions.md
    │   │   ├── ActionDefs.ts
    │   │   ├── AppContextDefs.ts
    │   │   ├── ComponentDefs.ts
    │   │   ├── ContainerDefs.ts
    │   │   ├── ExtensionDefs.ts
    │   │   ├── FunctionDefs.ts
    │   │   ├── RendererDefs.ts
    │   │   ├── scripting
    │   │   │   ├── BlockScope.ts
    │   │   │   ├── Compilation.ts
    │   │   │   ├── LogicalThread.ts
    │   │   │   ├── LoopScope.ts
    │   │   │   ├── modules.ts
    │   │   │   ├── ScriptParserError.ts
    │   │   │   ├── Token.ts
    │   │   │   ├── TryScope.ts
    │   │   │   └── TryScopeExp.ts
    │   │   └── ThemingDefs.ts
    │   ├── components
    │   │   ├── _conventions.md
    │   │   ├── abstractions.ts
    │   │   ├── Accordion
    │   │   │   ├── Accordion.md
    │   │   │   ├── Accordion.module.scss
    │   │   │   ├── Accordion.spec.ts
    │   │   │   ├── Accordion.tsx
    │   │   │   ├── AccordionContext.tsx
    │   │   │   ├── AccordionItem.tsx
    │   │   │   ├── AccordionItemNative.tsx
    │   │   │   └── AccordionNative.tsx
    │   │   ├── Animation
    │   │   │   └── AnimationNative.tsx
    │   │   ├── APICall
    │   │   │   ├── APICall.md
    │   │   │   ├── APICall.spec.ts
    │   │   │   ├── APICall.tsx
    │   │   │   └── APICallNative.tsx
    │   │   ├── App
    │   │   │   ├── App.md
    │   │   │   ├── App.module.scss
    │   │   │   ├── App.spec.ts
    │   │   │   ├── App.tsx
    │   │   │   ├── AppLayoutContext.ts
    │   │   │   ├── AppNative.tsx
    │   │   │   ├── AppStateContext.ts
    │   │   │   ├── doc-resources
    │   │   │   │   ├── condensed-sticky.xmlui
    │   │   │   │   ├── condensed.xmlui
    │   │   │   │   ├── horizontal-sticky.xmlui
    │   │   │   │   ├── horizontal.xmlui
    │   │   │   │   ├── vertical-full-header.xmlui
    │   │   │   │   ├── vertical-sticky.xmlui
    │   │   │   │   └── vertical.xmlui
    │   │   │   ├── IndexerContext.ts
    │   │   │   ├── LinkInfoContext.ts
    │   │   │   ├── SearchContext.tsx
    │   │   │   ├── Sheet.module.scss
    │   │   │   └── Sheet.tsx
    │   │   ├── AppHeader
    │   │   │   ├── AppHeader.md
    │   │   │   ├── AppHeader.module.scss
    │   │   │   ├── AppHeader.spec.ts
    │   │   │   ├── AppHeader.tsx
    │   │   │   └── AppHeaderNative.tsx
    │   │   ├── AppState
    │   │   │   ├── AppState.md
    │   │   │   ├── AppState.spec.ts
    │   │   │   ├── AppState.tsx
    │   │   │   └── AppStateNative.tsx
    │   │   ├── AutoComplete
    │   │   │   ├── AutoComplete.md
    │   │   │   ├── AutoComplete.module.scss
    │   │   │   ├── AutoComplete.spec.ts
    │   │   │   ├── AutoComplete.tsx
    │   │   │   ├── AutoCompleteContext.tsx
    │   │   │   └── AutoCompleteNative.tsx
    │   │   ├── Avatar
    │   │   │   ├── Avatar.md
    │   │   │   ├── Avatar.module.scss
    │   │   │   ├── Avatar.spec.ts
    │   │   │   ├── Avatar.tsx
    │   │   │   └── AvatarNative.tsx
    │   │   ├── Backdrop
    │   │   │   ├── Backdrop.md
    │   │   │   ├── Backdrop.module.scss
    │   │   │   ├── Backdrop.spec.ts
    │   │   │   ├── Backdrop.tsx
    │   │   │   └── BackdropNative.tsx
    │   │   ├── Badge
    │   │   │   ├── Badge.md
    │   │   │   ├── Badge.module.scss
    │   │   │   ├── Badge.spec.ts
    │   │   │   ├── Badge.tsx
    │   │   │   └── BadgeNative.tsx
    │   │   ├── Bookmark
    │   │   │   ├── Bookmark.md
    │   │   │   ├── Bookmark.module.scss
    │   │   │   ├── Bookmark.spec.ts
    │   │   │   ├── Bookmark.tsx
    │   │   │   └── BookmarkNative.tsx
    │   │   ├── Breakout
    │   │   │   ├── Breakout.module.scss
    │   │   │   ├── Breakout.spec.ts
    │   │   │   ├── Breakout.tsx
    │   │   │   └── BreakoutNative.tsx
    │   │   ├── Button
    │   │   │   ├── Button-style.spec.ts
    │   │   │   ├── Button.md
    │   │   │   ├── Button.module.scss
    │   │   │   ├── Button.spec.ts
    │   │   │   ├── Button.tsx
    │   │   │   └── ButtonNative.tsx
    │   │   ├── Card
    │   │   │   ├── Card.md
    │   │   │   ├── Card.module.scss
    │   │   │   ├── Card.spec.ts
    │   │   │   ├── Card.tsx
    │   │   │   └── CardNative.tsx
    │   │   ├── Carousel
    │   │   │   ├── Carousel.md
    │   │   │   ├── Carousel.module.scss
    │   │   │   ├── Carousel.spec.ts
    │   │   │   ├── Carousel.tsx
    │   │   │   ├── CarouselContext.tsx
    │   │   │   ├── CarouselItem.tsx
    │   │   │   ├── CarouselItemNative.tsx
    │   │   │   └── CarouselNative.tsx
    │   │   ├── ChangeListener
    │   │   │   ├── ChangeListener.md
    │   │   │   ├── ChangeListener.spec.ts
    │   │   │   ├── ChangeListener.tsx
    │   │   │   └── ChangeListenerNative.tsx
    │   │   ├── chart-color-schemes.ts
    │   │   ├── Charts
    │   │   │   ├── AreaChart
    │   │   │   │   ├── AreaChart.md
    │   │   │   │   ├── AreaChart.spec.ts
    │   │   │   │   ├── AreaChart.tsx
    │   │   │   │   └── AreaChartNative.tsx
    │   │   │   ├── BarChart
    │   │   │   │   ├── BarChart.md
    │   │   │   │   ├── BarChart.module.scss
    │   │   │   │   ├── BarChart.spec.ts
    │   │   │   │   ├── BarChart.tsx
    │   │   │   │   └── BarChartNative.tsx
    │   │   │   ├── DonutChart
    │   │   │   │   ├── DonutChart.spec.ts
    │   │   │   │   └── DonutChart.tsx
    │   │   │   ├── LabelList
    │   │   │   │   ├── LabelList.module.scss
    │   │   │   │   ├── LabelList.spec.ts
    │   │   │   │   ├── LabelList.tsx
    │   │   │   │   └── LabelListNative.tsx
    │   │   │   ├── Legend
    │   │   │   │   ├── Legend.spec.ts
    │   │   │   │   ├── Legend.tsx
    │   │   │   │   └── LegendNative.tsx
    │   │   │   ├── LineChart
    │   │   │   │   ├── LineChart.md
    │   │   │   │   ├── LineChart.module.scss
    │   │   │   │   ├── LineChart.spec.ts
    │   │   │   │   ├── LineChart.tsx
    │   │   │   │   └── LineChartNative.tsx
    │   │   │   ├── PieChart
    │   │   │   │   ├── PieChart.md
    │   │   │   │   ├── PieChart.spec.ts
    │   │   │   │   ├── PieChart.tsx
    │   │   │   │   ├── PieChartNative.module.scss
    │   │   │   │   └── PieChartNative.tsx
    │   │   │   ├── RadarChart
    │   │   │   │   ├── RadarChart.md
    │   │   │   │   ├── RadarChart.spec.ts
    │   │   │   │   ├── RadarChart.tsx
    │   │   │   │   └── RadarChartNative.tsx
    │   │   │   ├── Tooltip
    │   │   │   │   ├── TooltipContent.module.scss
    │   │   │   │   ├── TooltipContent.spec.ts
    │   │   │   │   └── TooltipContent.tsx
    │   │   │   └── utils
    │   │   │       ├── abstractions.ts
    │   │   │       └── ChartProvider.tsx
    │   │   ├── Checkbox
    │   │   │   ├── Checkbox.md
    │   │   │   ├── Checkbox.spec.ts
    │   │   │   └── Checkbox.tsx
    │   │   ├── CodeBlock
    │   │   │   ├── CodeBlock.module.scss
    │   │   │   ├── CodeBlock.spec.ts
    │   │   │   ├── CodeBlock.tsx
    │   │   │   ├── CodeBlockNative.tsx
    │   │   │   └── highlight-code.ts
    │   │   ├── collectedComponentMetadata.ts
    │   │   ├── ColorPicker
    │   │   │   ├── ColorPicker.md
    │   │   │   ├── ColorPicker.module.scss
    │   │   │   ├── ColorPicker.spec.ts
    │   │   │   ├── ColorPicker.tsx
    │   │   │   └── ColorPickerNative.tsx
    │   │   ├── Column
    │   │   │   ├── Column.md
    │   │   │   ├── Column.tsx
    │   │   │   ├── ColumnNative.tsx
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   └── TableContext.tsx
    │   │   ├── component-utils.ts
    │   │   ├── ComponentProvider.tsx
    │   │   ├── ComponentRegistryContext.tsx
    │   │   ├── container-helpers.tsx
    │   │   ├── ContentSeparator
    │   │   │   ├── ContentSeparator.md
    │   │   │   ├── ContentSeparator.module.scss
    │   │   │   ├── ContentSeparator.spec.ts
    │   │   │   ├── ContentSeparator.tsx
    │   │   │   └── ContentSeparatorNative.tsx
    │   │   ├── DataSource
    │   │   │   ├── DataSource.md
    │   │   │   └── DataSource.tsx
    │   │   ├── DateInput
    │   │   │   ├── DateInput.md
    │   │   │   ├── DateInput.module.scss
    │   │   │   ├── DateInput.spec.ts
    │   │   │   ├── DateInput.tsx
    │   │   │   └── DateInputNative.tsx
    │   │   ├── DatePicker
    │   │   │   ├── DatePicker.md
    │   │   │   ├── DatePicker.module.scss
    │   │   │   ├── DatePicker.spec.ts
    │   │   │   ├── DatePicker.tsx
    │   │   │   └── DatePickerNative.tsx
    │   │   ├── DropdownMenu
    │   │   │   ├── DropdownMenu.md
    │   │   │   ├── DropdownMenu.module.scss
    │   │   │   ├── DropdownMenu.spec.ts
    │   │   │   ├── DropdownMenu.tsx
    │   │   │   ├── DropdownMenuNative.tsx
    │   │   │   ├── MenuItem.md
    │   │   │   └── SubMenuItem.md
    │   │   ├── EmojiSelector
    │   │   │   ├── EmojiSelector.md
    │   │   │   ├── EmojiSelector.spec.ts
    │   │   │   ├── EmojiSelector.tsx
    │   │   │   └── EmojiSelectorNative.tsx
    │   │   ├── ExpandableItem
    │   │   │   ├── ExpandableItem.module.scss
    │   │   │   ├── ExpandableItem.spec.ts
    │   │   │   ├── ExpandableItem.tsx
    │   │   │   └── ExpandableItemNative.tsx
    │   │   ├── FileInput
    │   │   │   ├── FileInput.md
    │   │   │   ├── FileInput.module.scss
    │   │   │   ├── FileInput.spec.ts
    │   │   │   ├── FileInput.tsx
    │   │   │   └── FileInputNative.tsx
    │   │   ├── FileUploadDropZone
    │   │   │   ├── FileUploadDropZone.md
    │   │   │   ├── FileUploadDropZone.module.scss
    │   │   │   ├── FileUploadDropZone.spec.ts
    │   │   │   ├── FileUploadDropZone.tsx
    │   │   │   └── FileUploadDropZoneNative.tsx
    │   │   ├── FlowLayout
    │   │   │   ├── FlowLayout.md
    │   │   │   ├── FlowLayout.module.scss
    │   │   │   ├── FlowLayout.spec.ts
    │   │   │   ├── FlowLayout.spec.ts-snapshots
    │   │   │   │   └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
    │   │   │   ├── FlowLayout.tsx
    │   │   │   └── FlowLayoutNative.tsx
    │   │   ├── Footer
    │   │   │   ├── Footer.md
    │   │   │   ├── Footer.module.scss
    │   │   │   ├── Footer.spec.ts
    │   │   │   ├── Footer.tsx
    │   │   │   └── FooterNative.tsx
    │   │   ├── Form
    │   │   │   ├── Form.md
    │   │   │   ├── Form.module.scss
    │   │   │   ├── Form.spec.ts
    │   │   │   ├── Form.tsx
    │   │   │   ├── formActions.ts
    │   │   │   ├── FormContext.ts
    │   │   │   └── FormNative.tsx
    │   │   ├── FormItem
    │   │   │   ├── FormItem.md
    │   │   │   ├── FormItem.module.scss
    │   │   │   ├── FormItem.spec.ts
    │   │   │   ├── FormItem.tsx
    │   │   │   ├── FormItemNative.tsx
    │   │   │   ├── HelperText.module.scss
    │   │   │   ├── HelperText.tsx
    │   │   │   ├── ItemWithLabel.tsx
    │   │   │   └── Validations.ts
    │   │   ├── FormSection
    │   │   │   ├── FormSection.md
    │   │   │   ├── FormSection.ts
    │   │   │   └── FormSection.xmlui
    │   │   ├── Fragment
    │   │   │   ├── Fragment.spec.ts
    │   │   │   └── Fragment.tsx
    │   │   ├── Heading
    │   │   │   ├── abstractions.ts
    │   │   │   ├── H1.md
    │   │   │   ├── H1.spec.ts
    │   │   │   ├── H2.md
    │   │   │   ├── H2.spec.ts
    │   │   │   ├── H3.md
    │   │   │   ├── H3.spec.ts
    │   │   │   ├── H4.md
    │   │   │   ├── H4.spec.ts
    │   │   │   ├── H5.md
    │   │   │   ├── H5.spec.ts
    │   │   │   ├── H6.md
    │   │   │   ├── H6.spec.ts
    │   │   │   ├── Heading.md
    │   │   │   ├── Heading.module.scss
    │   │   │   ├── Heading.spec.ts
    │   │   │   ├── Heading.tsx
    │   │   │   └── HeadingNative.tsx
    │   │   ├── HoverCard
    │   │   │   ├── HoverCard.tsx
    │   │   │   └── HovercardNative.tsx
    │   │   ├── HtmlTags
    │   │   │   ├── HtmlTags.module.scss
    │   │   │   ├── HtmlTags.spec.ts
    │   │   │   └── HtmlTags.tsx
    │   │   ├── Icon
    │   │   │   ├── AdmonitionDanger.tsx
    │   │   │   ├── AdmonitionInfo.tsx
    │   │   │   ├── AdmonitionNote.tsx
    │   │   │   ├── AdmonitionTip.tsx
    │   │   │   ├── AdmonitionWarning.tsx
    │   │   │   ├── ApiIcon.tsx
    │   │   │   ├── ArrowDropDown.module.scss
    │   │   │   ├── ArrowDropDown.tsx
    │   │   │   ├── ArrowDropUp.module.scss
    │   │   │   ├── ArrowDropUp.tsx
    │   │   │   ├── ArrowLeft.module.scss
    │   │   │   ├── ArrowLeft.tsx
    │   │   │   ├── ArrowRight.module.scss
    │   │   │   ├── ArrowRight.tsx
    │   │   │   ├── Attach.tsx
    │   │   │   ├── Binding.module.scss
    │   │   │   ├── Binding.tsx
    │   │   │   ├── BoardIcon.tsx
    │   │   │   ├── BoxIcon.tsx
    │   │   │   ├── CheckIcon.tsx
    │   │   │   ├── ChevronDownIcon.tsx
    │   │   │   ├── ChevronLeft.tsx
    │   │   │   ├── ChevronRight.tsx
    │   │   │   ├── ChevronUpIcon.tsx
    │   │   │   ├── CodeFileIcon.tsx
    │   │   │   ├── CodeSandbox.tsx
    │   │   │   ├── CompactListIcon.tsx
    │   │   │   ├── ContentCopyIcon.tsx
    │   │   │   ├── DarkToLightIcon.tsx
    │   │   │   ├── DatabaseIcon.module.scss
    │   │   │   ├── DatabaseIcon.tsx
    │   │   │   ├── DocFileIcon.tsx
    │   │   │   ├── DocIcon.tsx
    │   │   │   ├── DotMenuHorizontalIcon.tsx
    │   │   │   ├── DotMenuIcon.tsx
    │   │   │   ├── EmailIcon.tsx
    │   │   │   ├── EmptyFolderIcon.tsx
    │   │   │   ├── ErrorIcon.tsx
    │   │   │   ├── ExpressionIcon.tsx
    │   │   │   ├── FillPlusCricleIcon.tsx
    │   │   │   ├── FilterIcon.tsx
    │   │   │   ├── FolderIcon.tsx
    │   │   │   ├── GlobeIcon.tsx
    │   │   │   ├── HomeIcon.tsx
    │   │   │   ├── HyperLinkIcon.tsx
    │   │   │   ├── Icon.md
    │   │   │   ├── Icon.module.scss
    │   │   │   ├── Icon.spec.ts
    │   │   │   ├── Icon.tsx
    │   │   │   ├── IconNative.tsx
    │   │   │   ├── ImageFileIcon.tsx
    │   │   │   ├── Inspect.tsx
    │   │   │   ├── LightToDark.tsx
    │   │   │   ├── LinkIcon.tsx
    │   │   │   ├── ListIcon.tsx
    │   │   │   ├── LooseListIcon.tsx
    │   │   │   ├── MoonIcon.tsx
    │   │   │   ├── MoreOptionsIcon.tsx
    │   │   │   ├── NoSortIcon.tsx
    │   │   │   ├── PDFIcon.tsx
    │   │   │   ├── PenIcon.tsx
    │   │   │   ├── PhoneIcon.tsx
    │   │   │   ├── PhotoIcon.tsx
    │   │   │   ├── PlusIcon.tsx
    │   │   │   ├── SearchIcon.tsx
    │   │   │   ├── ShareIcon.tsx
    │   │   │   ├── SortAscendingIcon.tsx
    │   │   │   ├── SortDescendingIcon.tsx
    │   │   │   ├── StarsIcon.tsx
    │   │   │   ├── SunIcon.tsx
    │   │   │   ├── svg
    │   │   │   │   ├── admonition_danger.svg
    │   │   │   │   ├── admonition_info.svg
    │   │   │   │   ├── admonition_note.svg
    │   │   │   │   ├── admonition_tip.svg
    │   │   │   │   ├── admonition_warning.svg
    │   │   │   │   ├── api.svg
    │   │   │   │   ├── arrow-dropdown.svg
    │   │   │   │   ├── arrow-left.svg
    │   │   │   │   ├── arrow-right.svg
    │   │   │   │   ├── arrow-up.svg
    │   │   │   │   ├── attach.svg
    │   │   │   │   ├── binding.svg
    │   │   │   │   ├── box.svg
    │   │   │   │   ├── bulb.svg
    │   │   │   │   ├── code-file.svg
    │   │   │   │   ├── code-sandbox.svg
    │   │   │   │   ├── dark_to_light.svg
    │   │   │   │   ├── database.svg
    │   │   │   │   ├── doc.svg
    │   │   │   │   ├── empty-folder.svg
    │   │   │   │   ├── expression.svg
    │   │   │   │   ├── eye-closed.svg
    │   │   │   │   ├── eye-dark.svg
    │   │   │   │   ├── eye.svg
    │   │   │   │   ├── file-text.svg
    │   │   │   │   ├── filter.svg
    │   │   │   │   ├── folder.svg
    │   │   │   │   ├── img.svg
    │   │   │   │   ├── inspect.svg
    │   │   │   │   ├── light_to_dark.svg
    │   │   │   │   ├── moon.svg
    │   │   │   │   ├── pdf.svg
    │   │   │   │   ├── photo.svg
    │   │   │   │   ├── share.svg
    │   │   │   │   ├── stars.svg
    │   │   │   │   ├── sun.svg
    │   │   │   │   ├── trending-down.svg
    │   │   │   │   ├── trending-level.svg
    │   │   │   │   ├── trending-up.svg
    │   │   │   │   ├── txt.svg
    │   │   │   │   ├── unknown-file.svg
    │   │   │   │   ├── unlink.svg
    │   │   │   │   └── xls.svg
    │   │   │   ├── TableDeleteColumnIcon.tsx
    │   │   │   ├── TableDeleteRowIcon.tsx
    │   │   │   ├── TableInsertColumnIcon.tsx
    │   │   │   ├── TableInsertRowIcon.tsx
    │   │   │   ├── TrashIcon.tsx
    │   │   │   ├── TrendingDownIcon.tsx
    │   │   │   ├── TrendingLevelIcon.tsx
    │   │   │   ├── TrendingUpIcon.tsx
    │   │   │   ├── TxtIcon.tsx
    │   │   │   ├── UnknownFileIcon.tsx
    │   │   │   ├── UnlinkIcon.tsx
    │   │   │   ├── UserIcon.tsx
    │   │   │   ├── WarningIcon.tsx
    │   │   │   └── XlsIcon.tsx
    │   │   ├── IconProvider.tsx
    │   │   ├── IconRegistryContext.tsx
    │   │   ├── IFrame
    │   │   │   ├── IFrame.md
    │   │   │   ├── IFrame.module.scss
    │   │   │   ├── IFrame.spec.ts
    │   │   │   ├── IFrame.tsx
    │   │   │   └── IFrameNative.tsx
    │   │   ├── Image
    │   │   │   ├── Image.md
    │   │   │   ├── Image.module.scss
    │   │   │   ├── Image.spec.ts
    │   │   │   ├── Image.tsx
    │   │   │   └── ImageNative.tsx
    │   │   ├── Input
    │   │   │   ├── index.ts
    │   │   │   ├── InputAdornment.module.scss
    │   │   │   ├── InputAdornment.tsx
    │   │   │   ├── InputDivider.module.scss
    │   │   │   ├── InputDivider.tsx
    │   │   │   ├── InputLabel.module.scss
    │   │   │   ├── InputLabel.tsx
    │   │   │   ├── PartialInput.module.scss
    │   │   │   └── PartialInput.tsx
    │   │   ├── InspectButton
    │   │   │   ├── InspectButton.module.scss
    │   │   │   └── InspectButton.tsx
    │   │   ├── Items
    │   │   │   ├── Items.md
    │   │   │   ├── Items.spec.ts
    │   │   │   ├── Items.tsx
    │   │   │   └── ItemsNative.tsx
    │   │   ├── Link
    │   │   │   ├── Link.md
    │   │   │   ├── Link.module.scss
    │   │   │   ├── Link.spec.ts
    │   │   │   ├── Link.tsx
    │   │   │   └── LinkNative.tsx
    │   │   ├── List
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   ├── List.md
    │   │   │   ├── List.module.scss
    │   │   │   ├── List.spec.ts
    │   │   │   ├── List.tsx
    │   │   │   └── ListNative.tsx
    │   │   ├── Logo
    │   │   │   ├── doc-resources
    │   │   │   │   └── xmlui-logo.svg
    │   │   │   ├── Logo.md
    │   │   │   ├── Logo.tsx
    │   │   │   └── LogoNative.tsx
    │   │   ├── Markdown
    │   │   │   ├── CodeText.module.scss
    │   │   │   ├── CodeText.tsx
    │   │   │   ├── Markdown.md
    │   │   │   ├── Markdown.module.scss
    │   │   │   ├── Markdown.spec.ts
    │   │   │   ├── Markdown.tsx
    │   │   │   ├── MarkdownNative.tsx
    │   │   │   ├── parse-binding-expr.ts
    │   │   │   └── utils.ts
    │   │   ├── metadata-helpers.ts
    │   │   ├── ModalDialog
    │   │   │   ├── ConfirmationModalContextProvider.tsx
    │   │   │   ├── Dialog.module.scss
    │   │   │   ├── Dialog.tsx
    │   │   │   ├── ModalDialog.md
    │   │   │   ├── ModalDialog.module.scss
    │   │   │   ├── ModalDialog.spec.ts
    │   │   │   ├── ModalDialog.tsx
    │   │   │   ├── ModalDialogNative.tsx
    │   │   │   └── ModalVisibilityContext.tsx
    │   │   ├── NavGroup
    │   │   │   ├── NavGroup.md
    │   │   │   ├── NavGroup.module.scss
    │   │   │   ├── NavGroup.spec.ts
    │   │   │   ├── NavGroup.tsx
    │   │   │   ├── NavGroupContext.ts
    │   │   │   └── NavGroupNative.tsx
    │   │   ├── NavLink
    │   │   │   ├── NavLink.md
    │   │   │   ├── NavLink.module.scss
    │   │   │   ├── NavLink.spec.ts
    │   │   │   ├── NavLink.tsx
    │   │   │   └── NavLinkNative.tsx
    │   │   ├── NavPanel
    │   │   │   ├── NavPanel.md
    │   │   │   ├── NavPanel.module.scss
    │   │   │   ├── NavPanel.spec.ts
    │   │   │   ├── NavPanel.tsx
    │   │   │   └── NavPanelNative.tsx
    │   │   ├── NestedApp
    │   │   │   ├── AppWithCodeView.module.scss
    │   │   │   ├── AppWithCodeView.tsx
    │   │   │   ├── AppWithCodeViewNative.tsx
    │   │   │   ├── defaultProps.tsx
    │   │   │   ├── logo.svg
    │   │   │   ├── NestedApp.module.scss
    │   │   │   ├── NestedApp.tsx
    │   │   │   ├── NestedAppNative.tsx
    │   │   │   ├── Tooltip.module.scss
    │   │   │   ├── Tooltip.tsx
    │   │   │   └── utils.ts
    │   │   ├── NoResult
    │   │   │   ├── NoResult.md
    │   │   │   ├── NoResult.module.scss
    │   │   │   ├── NoResult.spec.ts
    │   │   │   ├── NoResult.tsx
    │   │   │   └── NoResultNative.tsx
    │   │   ├── NumberBox
    │   │   │   ├── numberbox-abstractions.ts
    │   │   │   ├── NumberBox.md
    │   │   │   ├── NumberBox.module.scss
    │   │   │   ├── NumberBox.spec.ts
    │   │   │   ├── NumberBox.tsx
    │   │   │   └── NumberBoxNative.tsx
    │   │   ├── Option
    │   │   │   ├── Option.md
    │   │   │   ├── Option.spec.ts
    │   │   │   ├── Option.tsx
    │   │   │   ├── OptionNative.tsx
    │   │   │   └── OptionTypeProvider.tsx
    │   │   ├── PageMetaTitle
    │   │   │   ├── PageMetaTilteNative.tsx
    │   │   │   ├── PageMetaTitle.md
    │   │   │   ├── PageMetaTitle.spec.ts
    │   │   │   └── PageMetaTitle.tsx
    │   │   ├── Pages
    │   │   │   ├── Page.md
    │   │   │   ├── Pages.md
    │   │   │   ├── Pages.module.scss
    │   │   │   ├── Pages.tsx
    │   │   │   └── PagesNative.tsx
    │   │   ├── Pagination
    │   │   │   ├── Pagination.md
    │   │   │   ├── Pagination.module.scss
    │   │   │   ├── Pagination.spec.ts
    │   │   │   ├── Pagination.tsx
    │   │   │   └── PaginationNative.tsx
    │   │   ├── PositionedContainer
    │   │   │   ├── PositionedContainer.module.scss
    │   │   │   ├── PositionedContainer.tsx
    │   │   │   └── PositionedContainerNative.tsx
    │   │   ├── ProfileMenu
    │   │   │   ├── ProfileMenu.module.scss
    │   │   │   └── ProfileMenu.tsx
    │   │   ├── ProgressBar
    │   │   │   ├── ProgressBar.md
    │   │   │   ├── ProgressBar.module.scss
    │   │   │   ├── ProgressBar.spec.ts
    │   │   │   ├── ProgressBar.tsx
    │   │   │   └── ProgressBarNative.tsx
    │   │   ├── Queue
    │   │   │   ├── Queue.md
    │   │   │   ├── Queue.spec.ts
    │   │   │   ├── Queue.tsx
    │   │   │   ├── queueActions.ts
    │   │   │   └── QueueNative.tsx
    │   │   ├── RadioGroup
    │   │   │   ├── RadioGroup.md
    │   │   │   ├── RadioGroup.module.scss
    │   │   │   ├── RadioGroup.spec.ts
    │   │   │   ├── RadioGroup.tsx
    │   │   │   ├── RadioGroupNative.tsx
    │   │   │   ├── RadioItem.tsx
    │   │   │   └── RadioItemNative.tsx
    │   │   ├── RealTimeAdapter
    │   │   │   ├── RealTimeAdapter.tsx
    │   │   │   └── RealTimeAdapterNative.tsx
    │   │   ├── Redirect
    │   │   │   ├── Redirect.md
    │   │   │   ├── Redirect.spec.ts
    │   │   │   └── Redirect.tsx
    │   │   ├── ResponsiveBar
    │   │   │   ├── README.md
    │   │   │   ├── ResponsiveBar.md
    │   │   │   ├── ResponsiveBar.module.scss
    │   │   │   ├── ResponsiveBar.spec.ts
    │   │   │   ├── ResponsiveBar.tsx
    │   │   │   └── ResponsiveBarNative.tsx
    │   │   ├── Select
    │   │   │   ├── HiddenOption.tsx
    │   │   │   ├── OptionContext.ts
    │   │   │   ├── Select.md
    │   │   │   ├── Select.module.scss
    │   │   │   ├── Select.spec.ts
    │   │   │   ├── Select.tsx
    │   │   │   ├── SelectContext.tsx
    │   │   │   └── SelectNative.tsx
    │   │   ├── SelectionStore
    │   │   │   ├── SelectionStore.md
    │   │   │   ├── SelectionStore.tsx
    │   │   │   └── SelectionStoreNative.tsx
    │   │   ├── Slider
    │   │   │   ├── Slider.md
    │   │   │   ├── Slider.module.scss
    │   │   │   ├── Slider.spec.ts
    │   │   │   ├── Slider.tsx
    │   │   │   └── SliderNative.tsx
    │   │   ├── Slot
    │   │   │   ├── Slot.md
    │   │   │   ├── Slot.spec.ts
    │   │   │   └── Slot.ts
    │   │   ├── SlotItem.tsx
    │   │   ├── SpaceFiller
    │   │   │   ├── SpaceFiller.md
    │   │   │   ├── SpaceFiller.module.scss
    │   │   │   ├── SpaceFiller.spec.ts
    │   │   │   ├── SpaceFiller.tsx
    │   │   │   └── SpaceFillerNative.tsx
    │   │   ├── Spinner
    │   │   │   ├── Spinner.md
    │   │   │   ├── Spinner.module.scss
    │   │   │   ├── Spinner.spec.ts
    │   │   │   ├── Spinner.tsx
    │   │   │   └── SpinnerNative.tsx
    │   │   ├── Splitter
    │   │   │   ├── HSplitter.md
    │   │   │   ├── HSplitter.spec.ts
    │   │   │   ├── Splitter.md
    │   │   │   ├── Splitter.module.scss
    │   │   │   ├── Splitter.spec.ts
    │   │   │   ├── Splitter.tsx
    │   │   │   ├── SplitterNative.tsx
    │   │   │   ├── utils.ts
    │   │   │   ├── VSplitter.md
    │   │   │   └── VSplitter.spec.ts
    │   │   ├── Stack
    │   │   │   ├── CHStack.md
    │   │   │   ├── CHStack.spec.ts
    │   │   │   ├── CVStack.md
    │   │   │   ├── CVStack.spec.ts
    │   │   │   ├── HStack.md
    │   │   │   ├── HStack.spec.ts
    │   │   │   ├── Stack.md
    │   │   │   ├── Stack.module.scss
    │   │   │   ├── Stack.spec.ts
    │   │   │   ├── Stack.tsx
    │   │   │   ├── StackNative.tsx
    │   │   │   ├── VStack.md
    │   │   │   └── VStack.spec.ts
    │   │   ├── StickyBox
    │   │   │   ├── StickyBox.md
    │   │   │   ├── StickyBox.module.scss
    │   │   │   ├── StickyBox.tsx
    │   │   │   └── StickyBoxNative.tsx
    │   │   ├── Switch
    │   │   │   ├── Switch.md
    │   │   │   ├── Switch.spec.ts
    │   │   │   └── Switch.tsx
    │   │   ├── Table
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   ├── react-table-config.d.ts
    │   │   │   ├── Table.md
    │   │   │   ├── Table.module.scss
    │   │   │   ├── Table.spec.ts
    │   │   │   ├── Table.tsx
    │   │   │   ├── TableNative.tsx
    │   │   │   └── useRowSelection.tsx
    │   │   ├── TableOfContents
    │   │   │   ├── TableOfContents.module.scss
    │   │   │   ├── TableOfContents.spec.ts
    │   │   │   ├── TableOfContents.tsx
    │   │   │   └── TableOfContentsNative.tsx
    │   │   ├── Tabs
    │   │   │   ├── TabContext.tsx
    │   │   │   ├── TabItem.md
    │   │   │   ├── TabItem.tsx
    │   │   │   ├── TabItemNative.tsx
    │   │   │   ├── Tabs.md
    │   │   │   ├── Tabs.module.scss
    │   │   │   ├── Tabs.spec.ts
    │   │   │   ├── Tabs.tsx
    │   │   │   └── TabsNative.tsx
    │   │   ├── Text
    │   │   │   ├── Text.md
    │   │   │   ├── Text.module.scss
    │   │   │   ├── Text.spec.ts
    │   │   │   ├── Text.tsx
    │   │   │   └── TextNative.tsx
    │   │   ├── TextArea
    │   │   │   ├── TextArea.md
    │   │   │   ├── TextArea.module.scss
    │   │   │   ├── TextArea.spec.ts
    │   │   │   ├── TextArea.tsx
    │   │   │   ├── TextAreaNative.tsx
    │   │   │   ├── TextAreaResizable.tsx
    │   │   │   └── useComposedRef.ts
    │   │   ├── TextBox
    │   │   │   ├── TextBox.md
    │   │   │   ├── TextBox.module.scss
    │   │   │   ├── TextBox.spec.ts
    │   │   │   ├── TextBox.tsx
    │   │   │   └── TextBoxNative.tsx
    │   │   ├── Theme
    │   │   │   ├── NotificationToast.tsx
    │   │   │   ├── Theme.md
    │   │   │   ├── Theme.module.scss
    │   │   │   ├── Theme.spec.ts
    │   │   │   ├── Theme.tsx
    │   │   │   └── ThemeNative.tsx
    │   │   ├── TimeInput
    │   │   │   ├── TimeInput.md
    │   │   │   ├── TimeInput.module.scss
    │   │   │   ├── TimeInput.spec.ts
    │   │   │   ├── TimeInput.tsx
    │   │   │   ├── TimeInputNative.tsx
    │   │   │   └── utils.ts
    │   │   ├── Timer
    │   │   │   ├── Timer.md
    │   │   │   ├── Timer.spec.ts
    │   │   │   ├── Timer.tsx
    │   │   │   └── TimerNative.tsx
    │   │   ├── Toggle
    │   │   │   ├── Toggle.module.scss
    │   │   │   └── Toggle.tsx
    │   │   ├── ToneChangerButton
    │   │   │   ├── ToneChangerButton.md
    │   │   │   ├── ToneChangerButton.spec.ts
    │   │   │   └── ToneChangerButton.tsx
    │   │   ├── ToneSwitch
    │   │   │   ├── ToneSwitch.md
    │   │   │   ├── ToneSwitch.module.scss
    │   │   │   ├── ToneSwitch.spec.ts
    │   │   │   ├── ToneSwitch.tsx
    │   │   │   └── ToneSwitchNative.tsx
    │   │   ├── Tooltip
    │   │   │   ├── Tooltip.md
    │   │   │   ├── Tooltip.module.scss
    │   │   │   ├── Tooltip.spec.ts
    │   │   │   ├── Tooltip.tsx
    │   │   │   └── TooltipNative.tsx
    │   │   ├── Tree
    │   │   │   ├── testData.ts
    │   │   │   ├── Tree-dynamic.spec.ts
    │   │   │   ├── Tree-icons.spec.ts
    │   │   │   ├── Tree.md
    │   │   │   ├── Tree.spec.ts
    │   │   │   ├── TreeComponent.module.scss
    │   │   │   ├── TreeComponent.tsx
    │   │   │   └── TreeNative.tsx
    │   │   ├── TreeDisplay
    │   │   │   ├── TreeDisplay.md
    │   │   │   ├── TreeDisplay.module.scss
    │   │   │   ├── TreeDisplay.tsx
    │   │   │   └── TreeDisplayNative.tsx
    │   │   ├── ValidationSummary
    │   │   │   ├── ValidationSummary.module.scss
    │   │   │   └── ValidationSummary.tsx
    │   │   └── VisuallyHidden.tsx
    │   ├── components-core
    │   │   ├── abstractions
    │   │   │   ├── ComponentRenderer.ts
    │   │   │   ├── LoaderRenderer.ts
    │   │   │   ├── standalone.ts
    │   │   │   └── treeAbstractions.ts
    │   │   ├── action
    │   │   │   ├── actions.ts
    │   │   │   ├── APICall.tsx
    │   │   │   ├── FileDownloadAction.tsx
    │   │   │   ├── FileUploadAction.tsx
    │   │   │   ├── NavigateAction.tsx
    │   │   │   └── TimedAction.tsx
    │   │   ├── ApiBoundComponent.tsx
    │   │   ├── appContext
    │   │   │   ├── date-functions.ts
    │   │   │   ├── math-function.ts
    │   │   │   └── misc-utils.ts
    │   │   ├── AppContext.tsx
    │   │   ├── behaviors
    │   │   │   ├── Behavior.tsx
    │   │   │   └── CoreBehaviors.tsx
    │   │   ├── component-hooks.ts
    │   │   ├── ComponentDecorator.tsx
    │   │   ├── ComponentViewer.tsx
    │   │   ├── CompoundComponent.tsx
    │   │   ├── constants.ts
    │   │   ├── DebugViewProvider.tsx
    │   │   ├── descriptorHelper.ts
    │   │   ├── devtools
    │   │   │   ├── InspectorDialog.module.scss
    │   │   │   ├── InspectorDialog.tsx
    │   │   │   └── InspectorDialogVisibilityContext.tsx
    │   │   ├── EngineError.ts
    │   │   ├── event-handlers.ts
    │   │   ├── InspectorButton.module.scss
    │   │   ├── InspectorContext.tsx
    │   │   ├── interception
    │   │   │   ├── abstractions.ts
    │   │   │   ├── ApiInterceptor.ts
    │   │   │   ├── ApiInterceptorProvider.tsx
    │   │   │   ├── apiInterceptorWorker.ts
    │   │   │   ├── Backend.ts
    │   │   │   ├── Errors.ts
    │   │   │   ├── IndexedDb.ts
    │   │   │   ├── initMock.ts
    │   │   │   ├── InMemoryDb.ts
    │   │   │   ├── ReadonlyCollection.ts
    │   │   │   └── useApiInterceptorContext.tsx
    │   │   ├── loader
    │   │   │   ├── ApiLoader.tsx
    │   │   │   ├── DataLoader.tsx
    │   │   │   ├── ExternalDataLoader.tsx
    │   │   │   ├── Loader.tsx
    │   │   │   ├── MockLoaderRenderer.tsx
    │   │   │   └── PageableLoader.tsx
    │   │   ├── LoaderComponent.tsx
    │   │   ├── markup-check.ts
    │   │   ├── parts.ts
    │   │   ├── renderers.ts
    │   │   ├── rendering
    │   │   │   ├── AppContent.tsx
    │   │   │   ├── AppRoot.tsx
    │   │   │   ├── AppWrapper.tsx
    │   │   │   ├── buildProxy.ts
    │   │   │   ├── collectFnVarDeps.ts
    │   │   │   ├── ComponentAdapter.tsx
    │   │   │   ├── ComponentWrapper.tsx
    │   │   │   ├── Container.tsx
    │   │   │   ├── containers.ts
    │   │   │   ├── ContainerWrapper.tsx
    │   │   │   ├── ErrorBoundary.module.scss
    │   │   │   ├── ErrorBoundary.tsx
    │   │   │   ├── InvalidComponent.module.scss
    │   │   │   ├── InvalidComponent.tsx
    │   │   │   ├── nodeUtils.ts
    │   │   │   ├── reducer.ts
    │   │   │   ├── renderChild.tsx
    │   │   │   ├── StandaloneComponent.tsx
    │   │   │   ├── StateContainer.tsx
    │   │   │   ├── UnknownComponent.module.scss
    │   │   │   ├── UnknownComponent.tsx
    │   │   │   └── valueExtractor.ts
    │   │   ├── reportEngineError.ts
    │   │   ├── RestApiProxy.ts
    │   │   ├── script-runner
    │   │   │   ├── asyncProxy.ts
    │   │   │   ├── AttributeValueParser.ts
    │   │   │   ├── bannedFunctions.ts
    │   │   │   ├── BindingTreeEvaluationContext.ts
    │   │   │   ├── eval-tree-async.ts
    │   │   │   ├── eval-tree-common.ts
    │   │   │   ├── eval-tree-sync.ts
    │   │   │   ├── ParameterParser.ts
    │   │   │   ├── process-statement-async.ts
    │   │   │   ├── process-statement-common.ts
    │   │   │   ├── process-statement-sync.ts
    │   │   │   ├── ScriptingSourceTree.ts
    │   │   │   ├── simplify-expression.ts
    │   │   │   ├── statement-queue.ts
    │   │   │   └── visitors.ts
    │   │   ├── StandaloneApp.tsx
    │   │   ├── StandaloneExtensionManager.ts
    │   │   ├── TableOfContentsContext.tsx
    │   │   ├── theming
    │   │   │   ├── _themes.scss
    │   │   │   ├── component-layout-resolver.ts
    │   │   │   ├── extendThemeUtils.ts
    │   │   │   ├── hvar.ts
    │   │   │   ├── layout-resolver.ts
    │   │   │   ├── parse-layout-props.ts
    │   │   │   ├── StyleContext.tsx
    │   │   │   ├── StyleRegistry.ts
    │   │   │   ├── ThemeContext.tsx
    │   │   │   ├── ThemeProvider.tsx
    │   │   │   ├── themes
    │   │   │   │   ├── base-utils.ts
    │   │   │   │   ├── palette.ts
    │   │   │   │   ├── root.ts
    │   │   │   │   ├── solid.ts
    │   │   │   │   ├── theme-colors.ts
    │   │   │   │   └── xmlui.ts
    │   │   │   ├── themeVars.module.scss
    │   │   │   ├── themeVars.ts
    │   │   │   ├── transformThemeVars.ts
    │   │   │   └── utils.ts
    │   │   ├── utils
    │   │   │   ├── actionUtils.ts
    │   │   │   ├── audio-utils.ts
    │   │   │   ├── base64-utils.ts
    │   │   │   ├── compound-utils.ts
    │   │   │   ├── css-utils.ts
    │   │   │   ├── DataLoaderQueryKeyGenerator.ts
    │   │   │   ├── date-utils.ts
    │   │   │   ├── extractParam.ts
    │   │   │   ├── hooks.tsx
    │   │   │   ├── LruCache.ts
    │   │   │   ├── mergeProps.ts
    │   │   │   ├── misc.ts
    │   │   │   ├── request-params.ts
    │   │   │   ├── statementUtils.ts
    │   │   │   └── treeUtils.ts
    │   │   └── xmlui-parser.ts
    │   ├── index-standalone.ts
    │   ├── index.scss
    │   ├── index.ts
    │   ├── language-server
    │   │   ├── server-common.ts
    │   │   ├── server-web-worker.ts
    │   │   ├── server.ts
    │   │   ├── services
    │   │   │   ├── common
    │   │   │   │   ├── docs-generation.ts
    │   │   │   │   ├── lsp-utils.ts
    │   │   │   │   ├── metadata-utils.ts
    │   │   │   │   └── syntax-node-utilities.ts
    │   │   │   ├── completion.ts
    │   │   │   ├── diagnostic.ts
    │   │   │   ├── format.ts
    │   │   │   └── hover.ts
    │   │   └── xmlui-metadata-generated.js
    │   ├── logging
    │   │   ├── LoggerContext.tsx
    │   │   ├── LoggerInitializer.tsx
    │   │   ├── LoggerService.ts
    │   │   └── xmlui.ts
    │   ├── logo.svg
    │   ├── parsers
    │   │   ├── common
    │   │   │   ├── GenericToken.ts
    │   │   │   ├── InputStream.ts
    │   │   │   └── utils.ts
    │   │   ├── scripting
    │   │   │   ├── code-behind-collect.ts
    │   │   │   ├── Lexer.ts
    │   │   │   ├── modules.ts
    │   │   │   ├── Parser.ts
    │   │   │   ├── ParserError.ts
    │   │   │   ├── ScriptingNodeTypes.ts
    │   │   │   ├── TokenTrait.ts
    │   │   │   ├── TokenType.ts
    │   │   │   └── tree-visitor.ts
    │   │   ├── style-parser
    │   │   │   ├── errors.ts
    │   │   │   ├── source-tree.ts
    │   │   │   ├── StyleInputStream.ts
    │   │   │   ├── StyleLexer.ts
    │   │   │   ├── StyleParser.ts
    │   │   │   └── tokens.ts
    │   │   └── xmlui-parser
    │   │       ├── CharacterCodes.ts
    │   │       ├── diagnostics.ts
    │   │       ├── fileExtensions.ts
    │   │       ├── index.ts
    │   │       ├── lint.ts
    │   │       ├── parser.ts
    │   │       ├── ParserError.ts
    │   │       ├── scanner.ts
    │   │       ├── syntax-kind.ts
    │   │       ├── syntax-node.ts
    │   │       ├── transform.ts
    │   │       ├── utils.ts
    │   │       ├── xmlui-serializer.ts
    │   │       └── xmlui-tree.ts
    │   ├── react-app-env.d.ts
    │   ├── syntax
    │   │   ├── monaco
    │   │   │   ├── grammar.monacoLanguage.ts
    │   │   │   ├── index.ts
    │   │   │   ├── xmlui-dark.ts
    │   │   │   ├── xmlui-light.ts
    │   │   │   └── xmluiscript.monacoLanguage.ts
    │   │   └── textMate
    │   │       ├── index.ts
    │   │       ├── xmlui-dark.json
    │   │       ├── xmlui-light.json
    │   │       ├── xmlui.json
    │   │       └── xmlui.tmLanguage.json
    │   ├── testing
    │   │   ├── assertions.ts
    │   │   ├── component-test-helpers.ts
    │   │   ├── ComponentDrivers.ts
    │   │   ├── drivers
    │   │   │   ├── DateInputDriver.ts
    │   │   │   ├── index.ts
    │   │   │   ├── ModalDialogDriver.ts
    │   │   │   ├── NumberBoxDriver.ts
    │   │   │   ├── TextBoxDriver.ts
    │   │   │   ├── TimeInputDriver.ts
    │   │   │   ├── TimerDriver.ts
    │   │   │   └── TreeDriver.ts
    │   │   ├── fixtures.ts
    │   │   ├── index.ts
    │   │   ├── infrastructure
    │   │   │   ├── index.html
    │   │   │   ├── main.tsx
    │   │   │   ├── public
    │   │   │   │   ├── mockServiceWorker.js
    │   │   │   │   ├── resources
    │   │   │   │   │   ├── bell.svg
    │   │   │   │   │   ├── box.svg
    │   │   │   │   │   ├── doc.svg
    │   │   │   │   │   ├── eye.svg
    │   │   │   │   │   ├── flower-640x480.jpg
    │   │   │   │   │   ├── sun.svg
    │   │   │   │   │   ├── test-image-100x100.jpg
    │   │   │   │   │   └── txt.svg
    │   │   │   │   └── serve.json
    │   │   │   └── TestBed.tsx
    │   │   └── themed-app-test-helpers.ts
    │   └── vite-env.d.ts
    ├── tests
    │   ├── components
    │   │   ├── CodeBlock
    │   │   │   └── hightlight-code.test.ts
    │   │   ├── playground-pattern.test.ts
    │   │   └── Tree
    │   │       └── Tree-states.test.ts
    │   ├── components-core
    │   │   ├── abstractions
    │   │   │   └── treeAbstractions.test.ts
    │   │   ├── container
    │   │   │   └── buildProxy.test.ts
    │   │   ├── interception
    │   │   │   ├── orderBy.test.ts
    │   │   │   ├── ReadOnlyCollection.test.ts
    │   │   │   └── request-param-converter.test.ts
    │   │   ├── scripts-runner
    │   │   │   ├── AttributeValueParser.test.ts
    │   │   │   ├── eval-tree-arrow-async.test.ts
    │   │   │   ├── eval-tree-arrow.test.ts
    │   │   │   ├── eval-tree-func-decl-async.test.ts
    │   │   │   ├── eval-tree-func-decl.test.ts
    │   │   │   ├── eval-tree-pre-post.test.ts
    │   │   │   ├── eval-tree-regression.test.ts
    │   │   │   ├── eval-tree.test.ts
    │   │   │   ├── function-proxy.test.ts
    │   │   │   ├── parser-regression.test.ts
    │   │   │   ├── process-event.test.ts
    │   │   │   ├── process-function.test.ts
    │   │   │   ├── process-implicit-context.test.ts
    │   │   │   ├── process-statement-asgn.test.ts
    │   │   │   ├── process-statement-destruct.test.ts
    │   │   │   ├── process-statement-regs.test.ts
    │   │   │   ├── process-statement-sync.test.ts
    │   │   │   ├── process-statement.test.ts
    │   │   │   ├── process-switch-sync.test.ts
    │   │   │   ├── process-switch.test.ts
    │   │   │   ├── process-try-sync.test.ts
    │   │   │   ├── process-try.test.ts
    │   │   │   └── test-helpers.ts
    │   │   ├── test-metadata-handler.ts
    │   │   ├── theming
    │   │   │   ├── border-segments.test.ts
    │   │   │   ├── component-layout.resolver.test.ts
    │   │   │   ├── layout-property-parser.test.ts
    │   │   │   ├── layout-resolver.test.ts
    │   │   │   ├── layout-resolver2.test.ts
    │   │   │   ├── layout-vp-override.test.ts
    │   │   │   └── padding-segments.test.ts
    │   │   └── utils
    │   │       ├── date-utils.test.ts
    │   │       ├── format-human-elapsed-time.test.ts
    │   │       └── LruCache.test.ts
    │   ├── language-server
    │   │   ├── completion.test.ts
    │   │   ├── format.test.ts
    │   │   ├── hover.test.ts
    │   │   └── mockData.ts
    │   └── parsers
    │       ├── common
    │       │   └── input-stream.test.ts
    │       ├── markdown
    │       │   └── parse-binding-expression.test.ts
    │       ├── parameter-parser.test.ts
    │       ├── paremeter-parser.test.ts
    │       ├── scripting
    │       │   ├── eval-tree-arrow.test.ts
    │       │   ├── eval-tree-pre-post.test.ts
    │       │   ├── eval-tree.test.ts
    │       │   ├── function-proxy.test.ts
    │       │   ├── lexer-literals.test.ts
    │       │   ├── lexer-misc.test.ts
    │       │   ├── module-parse.test.ts
    │       │   ├── parser-arrow.test.ts
    │       │   ├── parser-assignments.test.ts
    │       │   ├── parser-binary.test.ts
    │       │   ├── parser-destructuring.test.ts
    │       │   ├── parser-errors.test.ts
    │       │   ├── parser-expressions.test.ts
    │       │   ├── parser-function.test.ts
    │       │   ├── parser-literals.test.ts
    │       │   ├── parser-primary.test.ts
    │       │   ├── parser-regex.test.ts
    │       │   ├── parser-statements.test.ts
    │       │   ├── parser-unary.test.ts
    │       │   ├── process-event.test.ts
    │       │   ├── process-implicit-context.test.ts
    │       │   ├── process-statement-asgn.test.ts
    │       │   ├── process-statement-destruct.test.ts
    │       │   ├── process-statement-regs.test.ts
    │       │   ├── process-statement-sync.test.ts
    │       │   ├── process-statement.test.ts
    │       │   ├── process-switch-sync.test.ts
    │       │   ├── process-switch.test.ts
    │       │   ├── process-try-sync.test.ts
    │       │   ├── process-try.test.ts
    │       │   ├── simplify-expression.test.ts
    │       │   ├── statement-hooks.test.ts
    │       │   └── test-helpers.ts
    │       ├── style-parser
    │       │   ├── generateHvarChain.test.ts
    │       │   ├── parseHVar.test.ts
    │       │   ├── parser.test.ts
    │       │   └── tokens.test.ts
    │       └── xmlui
    │           ├── lint.test.ts
    │           ├── parser.test.ts
    │           ├── scanner.test.ts
    │           ├── transform.attr.test.ts
    │           ├── transform.circular.test.ts
    │           ├── transform.element.test.ts
    │           ├── transform.errors.test.ts
    │           ├── transform.escape.test.ts
    │           ├── transform.regression.test.ts
    │           ├── transform.script.test.ts
    │           ├── transform.test.ts
    │           └── xmlui.ts
    ├── tests-e2e
    │   ├── api-bound-component-regression.spec.ts
    │   ├── api-call-as-extracted-component.spec.ts
    │   ├── assign-to-object-or-array-regression.spec.ts
    │   ├── binding-regression.spec.ts
    │   ├── children-as-template-context-vars.spec.ts
    │   ├── compound-component.spec.ts
    │   ├── context-vars-regression.spec.ts
    │   ├── data-bindings.spec.ts
    │   ├── datasource-and-api-usage-in-var.spec.ts
    │   ├── datasource-direct-binding.spec.ts
    │   ├── datasource-onLoaded-regression.spec.ts
    │   ├── modify-array-item-regression.spec.ts
    │   ├── namespaces.spec.ts
    │   ├── push-to-array-regression.spec.ts
    │   ├── screen-breakpoints.spec.ts
    │   ├── scripting.spec.ts
    │   ├── state-scope-in-pages.spec.ts
    │   └── state-var-scopes.spec.ts
    ├── tsconfig.bin.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    ├── vite.config.ts
    └── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/xmlui/src/testing/ComponentDrivers.ts:
--------------------------------------------------------------------------------

```typescript
import type { Locator, Page } from "@playwright/test";
import { getPseudoStyles } from "./component-test-helpers";

export type ComponentDriverParams = {
  locator: Locator;
  page: Page;
};

type ClickOption = {
  button?: "left" | "right" | "middle";
  clickCount?: number;
  delay?: number;
  force?: boolean;
  modifiers?: Array<"Alt" | "Control" | "ControlOrMeta" | "Meta" | "Shift">;
  noWaitAfter?: boolean;
  position?: {
    x: number;
    y: number;
  };
  timeout?: number;
  trial?: boolean;
};

export class ComponentDriver {
  protected readonly locator: Locator;
  protected readonly page: Page;

  constructor({ locator, page }: ComponentDriverParams) {
    this.locator = locator;
    this.page = page;
  }

  get component() {
    return this.locator;
  }

  getByTestId(testId: string): Locator {
    return this.component.getByTestId(testId).first();
  }

  getByPartName(part: string): Locator {
    return this.component.locator(`[data-part-id="${part}"]`).first();
  }

  getIcons(): Locator {
    return this.component.locator('[data-icon-name="*"]');
  }

  getIconsByName(name: string): Locator {
    return this.component.locator(`[data-icon-name="${name}"]`);
  }

  getIconByName(name: string): Locator {
    return this.component.locator(`[data-icon-name="${name}"]`).first();
  }

  /**
   * Gets the html tag name of the final rendered component
   */
  getComponentTagName() {
    return this.component.evaluate((el) => el.tagName.toLowerCase());
  }

  // NOTE: methods must be created using the arrow function notation.
  // Otherwise, the "this" will not be correctly bound to the class instance when destructuring.

  click = async (options?: ClickOption) => {
    await this.locator.click(options);
  };

  dblclick = async (options?: { timeout?: number }) => {
    await this.locator.dblclick(options);
  };

  focus = async (options?: { timeout?: number }) => {
    await this.locator.focus(options);
  };

  blur = async (options?: { timeout?: number }) => {
    await this.locator.blur(options);
  };

  hover = async (options?: { timeout?: number }) => {
    await this.locator.hover(options);
  };
}

export class InputComponentDriver extends ComponentDriver {
  get field() {
    return this.getByPartName("input");
  }

  get label() {
    return this.getByPartName("label");
  }

  get placeholder() {
    return this.field.getAttribute("placeholder");
  }

  get requiredIndicator() {
    return this.component.getByText("*");
  }
}

export class TestStateDriver {
  protected readonly testStateLocator: Locator;

  constructor(testStateLocator: Locator) {
    this.testStateLocator = testStateLocator;
  }

  /** returns an async function that can query the test state */
  get testState() {
    return async () => {
      const text = await this.testStateLocator.textContent();
      const testState = text === "undefined" ? undefined : JSON.parse(text!);
      return testState;
    };
  }
}

// --- Button

export class ButtonDriver extends ComponentDriver {
  // Ensure we either get rtl or ltr strings
  /* async getWritingDirection() {
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir
    const attribute = await this.locator.getAttribute("dir");
    if (attribute && attribute !== "auto") return attribute as "rtl" | "ltr";
    const style = await this.locator.evaluate(
      (element) => window.getComputedStyle(element).direction,
    );
    // Default is ltr: https://developer.mozilla.org/en-US/docs/Web/CSS/direction#values
    return style === "rtl" ? "rtl" : "ltr";
  } */

  // Unused as of yet
  /* async getTextNodes() {
    return await this.locator.evaluate((element) =>
      [...element.childNodes]
        .filter((e) => e.nodeType === Node.TEXT_NODE && e.textContent.trim())
        .map((e) => e.textContent.trim()),
    );
  }
 */
  getFirstNonTextNode() {
    return this.locator.locator("> *").first();
  }

  // NOTE: Accounts for icons being passed as children as well
  getIcons() {
    return this.locator.locator("> svg").or(this.locator.locator("> img"));
  }
}

// --- ContentSeparator

export class ContentSeparatorDriver extends ComponentDriver {
  get separator() {
    return this.component;
  }

  async getOrientation() {
    const classList = await this.separator.evaluate((el) => el.className);

    if (classList.includes("horizontal")) return "horizontal";
    if (classList.includes("vertical")) return "vertical";
    return "unknown";
  }

  async getComputedHeight() {
    return await this.separator.evaluate((el) => {
      return window.getComputedStyle(el).height;
    });
  }

  async getComputedWidth() {
    return await this.separator.evaluate((el) => {
      return window.getComputedStyle(el).width;
    });
  }

  async getBackgroundColor() {
    return await this.separator.evaluate((el) => {
      return window.getComputedStyle(el).backgroundColor;
    });
  }
}

// --- Avatar

export class AvatarDriver extends ComponentDriver {}

// --- Splitter

export class SplitterDriver extends ComponentDriver {
  /**
   * Gets the resizer element (non-floating)
   */
  async getResizer() {
    // Look for the non-floating resizer element
    const allResizerCandidates = this.locator.locator('[class*="resizer"]');
    const resizerCount = await allResizerCandidates.count();

    for (let i = 0; i < resizerCount; i++) {
      const candidate = allResizerCandidates.nth(i);
      const className = await candidate.getAttribute("class");
      // Look for resizer that doesn't contain "floating" in its class
      if (className && className.includes("resizer") && !className.includes("floating")) {
        return candidate;
      }
    }

    // Fallback: return first resizer element
    return allResizerCandidates.first();
  }

  /**
   * Gets the floating resizer element
   */
  async getFloatingResizer() {
    // Look for the floating resizer element
    const allResizerCandidates = this.locator.locator('[class*="resizer"]');
    const resizerCount = await allResizerCandidates.count();

    for (let i = 0; i < resizerCount; i++) {
      const candidate = allResizerCandidates.nth(i);
      const className = await candidate.getAttribute("class");
      // Look for resizer that contains "floating" in its class
      if (className && className.includes("floating")) {
        return candidate;
      }
    }

    // If no floating resizer found, return null locator
    return this.locator.locator('[class*="floating"][class*="resizer"]').first();
  }

  /**
   * Hovers near the resizer area to trigger floating resizer visibility
   */
  async hoverNearResizer() {
    // Get the center of the splitter and hover there
    const bounds = await this.locator.boundingBox();
    if (bounds) {
      await this.page.mouse.move(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
      // Wait a bit for the hover effect to take place
      await this.page.waitForTimeout(200);
    }
  }

  /**
   * Drags the resizer by the specified offset
   * @param deltaX - Horizontal offset in pixels
   * @param deltaY - Vertical offset in pixels
   */
  async dragResizer(deltaX: number, deltaY: number) {
    // Try to get both types of resizers
    const resizer = await this.getResizer();
    const floatingResizer = await this.getFloatingResizer();

    // Determine which resizer to use based on visibility
    let targetResizer = resizer;

    try {
      const isResizerVisible = await resizer.isVisible();
      const isFloatingResizerVisible = await floatingResizer.isVisible();

      if (!isResizerVisible && isFloatingResizerVisible) {
        targetResizer = floatingResizer;
      } else if (!isResizerVisible) {
        // If neither is visible, try to hover to make floating resizer visible
        await this.hoverNearResizer();
        targetResizer = floatingResizer;
      }
    } catch (error) {
      // If there's any error checking visibility, use the regular resizer
      targetResizer = resizer;
    }

    // Get the resizer bounds
    const resizerBounds = await targetResizer.boundingBox();
    if (!resizerBounds) {
      throw new Error("Could not get resizer bounds for drag operation");
    }

    const startX = resizerBounds.x + resizerBounds.width / 2;
    const startY = resizerBounds.y + resizerBounds.height / 2;
    const endX = startX + deltaX;
    const endY = startY + deltaY;

    // Perform the drag operation
    await this.page.mouse.move(startX, startY);
    await this.page.mouse.down();
    await this.page.mouse.move(endX, endY, { steps: 5 });
    await this.page.mouse.up();

    // Wait a bit for the component to update
    await this.page.waitForTimeout(100);
  }

  /**
   * Gets the primary panel element
   */
  getPrimaryPanel() {
    return this.locator.locator('[class*="primaryPanel"]').first();
  }

  /**
   * Gets the secondary panel element
   */
  getSecondaryPanel() {
    return this.locator.locator('[class*="secondaryPanel"]').first();
  }
}

// --- ExpandableItem

export class ExpandableItemDriver extends ComponentDriver {
  getSummary() {
    return this.component.locator('[class*="_summary_"]');
  }

  getSummaryContent() {
    return this.component.locator('[class*="_summaryContent_"]');
  }

  getContent() {
    return this.component.locator('[class*="_content_"]');
  }

  getIcon() {
    return this.component.locator('[class*="_icon_"] svg');
  }

  getSwitch() {
    // Get the actual switch input element, not the wrapper
    return this.component.getByRole("switch");
  }

  async isExpanded() {
    return await this.component.locator('[class*="_content_"]').isVisible();
  }

  async isDisabled() {
    return await this.component.evaluate((el) => el.className.includes("disabled"));
  }

  async expand() {
    if (!(await this.isExpanded())) {
      await this.getSummary().click();
    }
  }

  async collapse() {
    if (await this.isExpanded()) {
      await this.getSummary().click();
    }
  }

  async toggle() {
    await this.getSummary().click();
  }
}

// --- FileInput

export class FileInputDriver extends ComponentDriver {
  getTextBox() {
    return this.component.locator("input[readonly]");
  }

  getHiddenInput() {
    return this.component.locator('input[type="file"]');
  }

  getBrowseButton() {
    return this.component.locator('[class*="_button_"]');
  }

  getContainer() {
    return this.component;
  }

  async isEnabled() {
    const browseButton = this.getBrowseButton();
    return !(await browseButton.isDisabled());
  }

  async getSelectedFiles() {
    const textBox = this.getTextBox();
    const value = await textBox.inputValue();
    return value || "";
  }

  async openFileDialog() {
    await this.getBrowseButton().click();
  }

  async getPlaceholder() {
    const textBox = this.getTextBox();
    return (await textBox.getAttribute("placeholder")) || "";
  }

  async focusButton() {
    await this.getBrowseButton().focus();
  }

  async hasReadOnlyAttribute() {
    const textBox = this.getTextBox();
    return (await textBox.getAttribute("readonly")) !== null;
  }

  async getAcceptedFileTypes() {
    const hiddenInput = this.getHiddenInput();
    return (await hiddenInput.getAttribute("accept")) || "";
  }

  async isMultiple() {
    const hiddenInput = this.getHiddenInput();
    return (await hiddenInput.getAttribute("multiple")) !== null;
  }

  async isDirectory() {
    const hiddenInput = this.getHiddenInput();
    return (await hiddenInput.getAttribute("webkitdirectory")) !== null;
  }
}

// --- FileUploadDropZone

export class FileUploadDropZoneDriver extends ComponentDriver {
  getWrapper() {
    return this.component.locator('[class*="_wrapper_"]');
  }

  getHiddenInput() {
    return this.component.locator('input[type="file"]');
  }

  getDropPlaceholder() {
    return this.component.locator('[class*="_dropPlaceholder_"]');
  }

  getDropIcon() {
    return this.getDropPlaceholder().locator("svg");
  }

  async isDropPlaceholderVisible() {
    return await this.getDropPlaceholder().isVisible();
  }

  async isEnabled() {
    const input = this.getHiddenInput();
    const isDisabled = await input.isDisabled();
    return !isDisabled;
  }

  async getDropText() {
    return (await this.getDropPlaceholder().textContent()) || "";
  }

  async triggerDragEnter() {
    await this.component.dispatchEvent("dragenter");
  }

  async triggerDragLeave() {
    await this.component.dispatchEvent("dragleave");
  }

  async triggerDrop(files: string[] = ["test.txt"]) {
    // Simulate file drop event by creating File objects and using setInputFiles
    const hiddenInput = this.getHiddenInput();

    // Create temporary files for testing
    const fileObjects = files.map((name) => {
      return {
        name,
        mimeType: "text/plain",
        buffer: Buffer.from("test content"),
      };
    });

    // Set files on the hidden input
    await hiddenInput.setInputFiles(fileObjects);

    // Trigger the drop event with a proper structure
    await this.component.evaluate((element, fileNames) => {
      // Create a proper drop event
      const event = new DragEvent("drop", {
        bubbles: true,
        cancelable: true,
        dataTransfer: new DataTransfer(),
      });

      // Add files to dataTransfer if needed for component logic
      fileNames.forEach((fileName: string) => {
        const file = new File(["test content"], fileName, { type: "text/plain" });
        event.dataTransfer?.items.add(file);
      });

      element.dispatchEvent(event);
    }, files);
  }

  async triggerPaste() {
    await this.component.dispatchEvent("paste", {
      clipboardData: {
        files: [{ name: "pasted.txt", type: "text/plain", size: 50 }],
        items: [{ kind: "file", getAsFile: () => ({ name: "pasted.txt" }) }],
      },
    });
  }

  async hasChildren() {
    const childrenCount = await this.component
      .locator('> *:not(input):not([class*="_dropPlaceholder_"])')
      .count();
    return childrenCount > 0;
  }
}

// --- Form

type SubmitTrigger = "click" | "keypress";
type MockExternalApiOptions = {
  status?: number;
  headers?: Record<string, string>;
  body?: Record<string, any>;
};

export class BackdropDriver extends ComponentDriver {
  getBackdrop() {
    return this.component.locator("> *").first();
  }

  getOverlay() {
    return this.component.locator("> *").nth(1);
  }

  getDefaultBackgroundColor() {
    return "rgb(0, 0, 0)"; // Default backdrop color
  }

  getDefaultOpacity() {
    return "0.1"; // Default backdrop opacity
  }
}

export class FormDriver extends ComponentDriver {
  async mockExternalApi(url: string, apiOptions: MockExternalApiOptions) {
    const { status = 200, headers = {}, body = {} } = apiOptions;
    await this.page.route(url, (route) =>
      route.fulfill({ status, headers, body: JSON.stringify(body) }),
    );
  }

  get submitButton() {
    return this.getByPartName("submitButton");
  }

  get cancelButton() {
    return this.getByPartName("cancelButton");
  }

  async hasSubmitButton() {
    return (await this.submitButton.count()) > 0;
  }

  async submitForm(trigger: SubmitTrigger = "click") {
    if (trigger === "keypress") {
      if ((await this.hasSubmitButton()) && (await this.submitButton.isEnabled())) {
        await this.submitButton.focus();
      }
      await this.locator.locator("input").waitFor();
      const firstInputChild = this.locator.locator("input");
      if ((await firstInputChild.count()) > 0) {
        await firstInputChild.first().focus();
      }
      await this.page.keyboard.press("Enter");
    } else if (trigger === "click") {
      await this.submitButton.click();
    }
  }

  async getSubmitRequest(
    endpoint = "/entities",
    requestMethod = "POST",
    trigger: SubmitTrigger = "click",
    timeout = 5000,
  ) {
    const requestPromise = this.page.waitForRequest(
      (request) =>
        request.url().includes(endpoint) &&
        request.method().toLowerCase() === requestMethod.toLowerCase(),
      { timeout },
    );
    await this.submitForm(trigger);
    return requestPromise;
  }

  getSubmitResponse(endpoint = "/entities", responseStatus = 200, timeout = 5000) {
    const responsePromise = this.page.waitForResponse(
      (response) => response.url().includes(endpoint) && response.status() === responseStatus,
      { timeout },
    );
    return responsePromise;
  }

  /**
   * Gets the validation summary component inside the Form.
   * Uses the 'data-validation-summary' attribute to find the component
   */
  getValidationSummary() {
    return this.component.locator("[data-validation-summary='true']");
  }

  /**
   * Gets the validation display components inside the Form.
   * Uses the 'data-validation-display-severity' attribute to find the components.
   * The attribute contains the severity of the validation.
   */
  getValidationDisplays() {
    return this.component
      .locator("[data-validation-summary='true']")
      .locator("[data-validation-display-severity]");
  }

  getValidationDisplaysBySeverity(severity: string) {
    return this.component
      .locator("[data-validation-summary='true']")
      .locator(`[data-validation-display-severity="${severity}"]`);
  }

  // TODO: it would be a nice to have features to get validation displays and map them automatically
  /* async getValidationDisplaysAsDriver() {
    const displays = await this.getValidationDisplays();
    const displayList: ValidationDisplayDriver[] = [];

    const displayNum = await displays.count();
    for (let i = 0; i < displayNum; i++) {
      const element = displays.nth(i);
      displayList.push(new ValidationDisplayDriver({ locator: element, page: this.page }));
    }
    return displayList;
  } */
}

// --- ValidationSummary

export class ValidationSummaryDriver extends ComponentDriver {
  /**
   * Gets the validation display components inside the Form.
   * Uses the 'data-validation-display-severity' attribute to find the components.
   * The attribute contains the severity of the validation.
   */
  getValidationDisplays() {
    return this.component
      .locator("[data-validation-summary='true']")
      .locator("[data-validation-display-severity]");
  }
}

// --- ValidationDisplay

export class ValidationDisplayDriver extends ComponentDriver {
  getSeverity() {
    return this.component.getAttribute("data-validation-display-severity");
  }

  getText() {
    return this.component.locator("li").textContent();
  }
}

// --- Markdown

export class MarkdownDriver extends ComponentDriver {
  async hasHtmlElement(elements: string | string[]) {
    const contents = await this.component.innerHTML();
    elements = typeof elements === "string" ? [elements] : elements;
    return elements.map((e) => `<${e}`).reduce((acc, curr) => acc && contents.includes(curr), true);
  }
}

// --- Items

export class ItemsDriver extends ComponentDriver {}

// --- Range

export class RangeDriver extends ComponentDriver {}

// --- DatePicker

export class DatePickerDriver extends ComponentDriver {
  async toggleDropdownVisibility() {
    await this.component.click();
  }

  async pickADay(value: string) {
    await this.component
      .getByRole("gridcell", { name: value })
      .or(this.page.getByRole("gridcell", { name: value }))
      .first()
      .click({ force: true });
  }
}

// --- AutoComplete

export class AutoCompleteDriver extends ComponentDriver {
  async toggleOptionsVisibility() {
    await this.component.click();
  }

  async selectLabel(value: string) {
    await this.component
      .getByRole("option", { name: value })
      .or(this.page.getByRole("option", { name: value }))
      .first()
      .click({ force: true });
  }

  async searchFor(value: string) {
    await this.page.getByRole("combobox").fill(value);
  }

  async chooseIndex(index: number) {
    await this.locator
      .getByRole("option")
      .nth(index)
      .or(this.page.getByRole("option").nth(index))
      .first()
      .click();
  }
}

// --- Select

export class SelectDriver extends ComponentDriver {
  async toggleOptionsVisibility() {
    await this.component.click();
  }

  async selectLabel(value: string) {
    await this.component
      .getByRole("option", { name: value })
      .or(this.page.getByRole("option", { name: value }))
      .first()
      .click({ force: true });
  }

  async selectFirstLabelPostSearh(label: string) {
    await this.searchFor(label);
    await this.chooseIndex(0);
  }

  async searchFor(value: string) {
    await this.page.getByRole("searchbox").fill(value);
  }

  async chooseIndex(index: number) {
    await this.locator
      .getByRole("option")
      .nth(index)
      .or(this.page.getByRole("option").nth(index))
      .first()
      .click();
  }

  async selectMultipleLabels(values: string[]) {
    for (const value of values) {
      await this.component
        .getByRole("option", { name: value })
        .or(this.page.getByRole("option", { name: value }))
        .first()
        .click();
    }
  }
}

// --- RadioGroup

export class RadioGroupDriver extends ComponentDriver {}

// --- TextArea

export class TextAreaDriver extends InputComponentDriver {}

// --- ProgressBar

export class ProgressBarDriver extends ComponentDriver {
  get bar() {
    return this.component.locator("> div");
  }

  async getProgressRatio() {
    const style = await this.bar.getAttribute("style");
    if (!style) return 0;

    const widthMatch = style.match(/width:\s*(\d+(?:\.\d+)?)%/);
    return widthMatch ? parseFloat(widthMatch[1]) / 100 : 0;
  }
}

// --- List

export class ListDriver extends ComponentDriver {
  /**
   * Gets all list item elements
   */
  get items() {
    return this.component
      .locator("[data-list-item]")
      .or(this.component.locator("div").filter({ hasText: /^(?!.*Loading).*/ }));
  }

  /**
   * Gets the loading spinner element
   */
  get loadingSpinner() {
    return this.component.locator('[class*="loadingWrapper"]');
  }

  /**
   * Checks if the loading state is visible
   */
  async isLoading() {
    return await this.loadingSpinner.isVisible().catch(() => false);
  }

  /**
   * Gets group header elements
   */
  get groupHeaders() {
    return this.component
      .locator("[data-group-header]")
      .or(this.component.locator("div").filter({ hasText: /^Category:|^Header:/ }));
  }

  /**
   * Gets group footer elements
   */
  get groupFooters() {
    return this.component
      .locator("[data-group-footer]")
      .or(this.component.locator("div").filter({ hasText: /^End of/ }));
  }

  /**
   * Gets the empty state element
   */
  get emptyState() {
    return this.component
      .locator("[data-empty-state]")
      .or(this.component.getByText("No items found!"));
  }

  /**
   * Scrolls the list to a specific position
   */
  async scrollTo(position: "top" | "bottom" | number) {
    if (position === "top") {
      await this.component.evaluate((el) => el.scrollTo({ top: 0 }));
    } else if (position === "bottom") {
      await this.component.evaluate((el) => el.scrollTo({ top: el.scrollHeight }));
    } else {
      await this.component.evaluate((el, pos) => el.scrollTo({ top: pos }), position);
    }
  }

  /**
   * Gets the number of visible items (for virtualization testing)
   */
  async getVisibleItemCount() {
    return await this.items.count();
  }

  /**
   * Gets item at specific index
   */
  getItemAt(index: number) {
    return this.items.nth(index);
  }

  /**
   * Gets item by text content
   */
  getItemByText(text: string) {
    return this.component.getByText(text);
  }

  /**
   * Checks if list is empty
   */
  async isEmpty() {
    // Check for the actual "No data available" text that appears when list is empty
    const noDataText = await this.component.textContent();
    const hasNoDataMessage =
      noDataText?.includes("No data available") || noDataText?.includes("No items found");

    // Also check if there are any actual data items
    const itemCount = await this.items.count();
    const hasDataItems = itemCount > 0 && !hasNoDataMessage;

    return hasNoDataMessage || !hasDataItems;
  }

  /**
   * Gets all text content from visible items
   */
  async getVisibleItemTexts() {
    const items = await this.items.all();
    const texts = await Promise.all(items.map((item) => item.textContent()));
    return texts.filter((text) => text !== null);
  }
}

// --- Text

export class TextDriver extends ComponentDriver {}

// --- Heading

export class HeadingDriver extends ComponentDriver {}

// --- Icon

export class IconDriver extends ComponentDriver {
  get svgIcon() {
    return this.component.locator("svg");
  }
}

// --- Stack

export class StackDriver extends ComponentDriver {}

// --- HStack

export class HStackDriver extends StackDriver {}

// --- VStack

export class VStackDriver extends StackDriver {}

// --- Link

export class LinkDriver extends ComponentDriver {}

// --- NavLink

export class NavLinkDriver extends ComponentDriver {}

// --- NavGroup

export class NavGroupDriver extends ComponentDriver {
  getIcons() {
    return this.locator.locator("> svg").or(this.locator.locator("> img"));
  }
}

// --- NavPanel

export class NavPanelDriver extends ComponentDriver {}

// --- Card

export class CardDriver extends ComponentDriver {
  get avatar() {
    return this.component.getByRole("img", { name: "avatar" });
  }
}

// --- Accordion

export class AccordionDriver extends ComponentDriver {}

// --- AppHeader

export class AppHeaderDriver extends ComponentDriver {}

// --- AppFooter

export class AppFooterDriver extends ComponentDriver {}

// --- Badge

export class BadgeDriver extends ComponentDriver {}

// --- NoResult

export class NoResultDriver extends ComponentDriver {}

// --- Option

export class OptionDriver extends ComponentDriver {}

// --- FormItem

// NOTE (NEW): FormItem will be removed, delete this Driver
// NOTE: Do not delete these comments.
// This is an untested proposal to shorten code length.
// Now, you have to provide the .input element for a specific control driver:
//
// element = FormItemDriver().input -> TextBoxDriver(element)
//
// This can be shortened to FormItemDriver().input.as("TextBoxDriver") if we extend
// the locator object only for the return type of the "input" getter to support the "as" method
//
/* type DriverMap = {
  TextBoxDriver: TextBoxDriver;
  NumberBoxDriver: NumberBoxDriver;
};

const driverConstructors: {
  [K in keyof DriverMap]: new (obj: ComponentDriverParams) => DriverMap[K]
} = {
  TextBoxDriver,
  NumberBoxDriver
};

export interface ExtendedLocator extends Locator {
  as?: <K extends keyof DriverMap>(type: K) => DriverMap[K];
}

export const extendLocator = (
  locator: Locator,
): ExtendedLocator => ({
  ...locator,
  locator: (selector, options) =>
    extendLocator(locator.locator(selector, options)),
  as: (type) => { return new driverConstructors[type]({ locator, page: locator.page() }); }
}); */

export class FormItemDriver extends ComponentDriver {
  get input() {
    return this.component.locator(">input").or(this.component).first();
  }

  get textBox() {
    return this.input.getByRole("textbox");
  }

  get checkbox() {
    return this.input.getByRole("checkbox");
  }

  get label() {
    return this.component.locator("label");
  }

  get validationStatusTag() {
    return "data-validation-status";
  }

  get validationStatusIndicator() {
    return this.component.locator(`[${this.validationStatusTag}]`);
  }
}

// --- htmlTags

export class HtmlTagDriver extends ComponentDriver {}

// --- CodeBlock

export class CodeBlockDriver extends ComponentDriver {
  getHeader() {
    return this.component.locator('[class*="codeBlockHeader"]');
  }

  getContent() {
    return this.component.locator('[class*="codeBlockContent"]');
  }

  getCopyButton() {
    return this.component.locator('[class*="codeBlockCopyButton"] button');
  }

  getFilename() {
    return this.getHeader().locator("span");
  }

  async isCopyButtonVisible() {
    return await this.getCopyButton().isVisible();
  }

  async hasHeader() {
    return await this.getHeader().isVisible();
  }

  async getCodeText() {
    return await this.getContent().textContent();
  }

  async clickCopyButton() {
    await this.getCopyButton().click();
  }

  async hoverContent() {
    await this.getContent().hover();
  }
}

// --- Checkbox

export class CheckboxDriver extends InputComponentDriver {
  async getIndicatorColor() {
    const specifier = this.component.getByRole("checkbox").or(this.component).last();
    const { boxShadow } = await getPseudoStyles(specifier, "::before", "box-shadow");
    const colorMatch = boxShadow.match(/(rgba?\([^)]+\)|hsla?\([^)]+\)|#[a-fA-F0-9]{3,8})/);
    return colorMatch ? colorMatch[1] : null;
  }
}

// --- Label

export class LabelDriver extends ComponentDriver {}

// --- Spinner

export class SpinnerDriver extends ComponentDriver {
  /**
   * Gets the main spinner element (the one with the ring animation)
   */
  get spinnerElement() {
    // Filter for spinner elements and use the first one by default
    return this.page
      .locator('[data-testid="test-id-component"]')
      .filter({ has: this.page.locator("div") })
      .first();
  }

  /**
   * Gets a specific spinner element by index (for multiple spinners)
   */
  getSpinnerByIndex(index: number) {
    return this.page
      .locator('[data-testid="test-id-component"]')
      .filter({ has: this.page.locator("div") })
      .nth(index);
  }

  /**
   * Gets the fullscreen wrapper element (only exists when fullScreen=true)
   */
  get fullScreenWrapper() {
    return this.page.locator('div[class*="_fullScreenSpinnerWrapper_"]');
  }

  /**
   * Checks if the spinner is in fullscreen mode
   */
  async isFullScreen(): Promise<boolean> {
    try {
      const wrapper = this.fullScreenWrapper;
      return await wrapper.isVisible();
    } catch {
      return false;
    }
  }

  /**
   * Gets the computed style of the spinner element
   */
  async getSpinnerStyle() {
    const element = this.spinnerElement;
    return await element.evaluate((el: HTMLElement) => {
      const styles = window.getComputedStyle(el);
      return {
        display: styles.display,
        position: styles.position,
        width: styles.width,
        height: styles.height,
        animationDuration: styles.animationDuration,
        className: el.className,
      };
    });
  }

  /**
   * Gets the animation duration from the child elements (where the actual animation occurs)
   */
  async getAnimationDuration(): Promise<string> {
    const element = this.spinnerElement;
    return await element.evaluate((el: HTMLElement) => {
      // Check the first child div for animation duration
      const firstChild = el.querySelector("div");
      if (firstChild) {
        const styles = window.getComputedStyle(firstChild);
        return styles.animationDuration;
      }
      return "0s";
    });
  }

  /**
   * Waits for the spinner to appear
   */
  async waitForSpinner(timeout: number = 5000): Promise<void> {
    await this.spinnerElement.waitFor({ state: "visible", timeout });
  }

  /**
   * Waits for the spinner to disappear
   */
  async waitForSpinnerToDisappear(timeout: number = 5000): Promise<void> {
    await this.spinnerElement.waitFor({ state: "hidden", timeout });
  }

  /**
   * Checks if the spinner is visible
   */
  async isVisible(): Promise<boolean> {
    return await this.spinnerElement.isVisible();
  }

  /**
   * Gets the CSS class name to verify CSS modules are working
   */
  async getClassName(): Promise<string> {
    return (await this.spinnerElement.getAttribute("class")) || "";
  }

  /**
   * Gets the number of child div elements (should be 4 for the ring animation)
   */
  async getChildCount(): Promise<number> {
    return await this.spinnerElement.evaluate((el: HTMLElement) => {
      return el.querySelectorAll("div").length;
    });
  }

  /**
   * Gets the total number of spinner components on the page
   */
  async getSpinnerCount(): Promise<number> {
    return await this.page.locator('[data-testid="test-id-component"]').count();
  }

  /**
   * Gets a spinner by specific test ID
   */
  getSpinnerByTestId(testId: string) {
    return this.page.locator(`[data-testid="${testId}"]`);
  }

  /**
   * Checks if fullscreen mode has the correct wrapper structure
   */
  async getFullScreenWrapperInfo() {
    const wrapper = this.fullScreenWrapper;
    if (!(await wrapper.isVisible())) {
      return null;
    }

    return await wrapper.evaluate((el: HTMLElement) => {
      const parent = el.parentElement;
      const styles = window.getComputedStyle(el);
      return {
        position: styles.position,
        inset: styles.inset,
        parentClassName: parent?.className || "",
        hasSpinnerChild: !!el.querySelector('[class*="_lds-ring_"]'),
      };
    });
  }
}

// --- DropdownMenu

export class DropdownMenuDriver extends ComponentDriver {
  /**
   * Get the trigger button element
   * For DropdownMenu, we'll look for the button on the page level since Radix UI may render it separately
   */
  getTrigger() {
    return this.page.getByRole("button").first();
  }

  /**
   * Open the dropdown menu
   */
  async open() {
    const trigger = this.getTrigger();
    await trigger.click();
  }

  /**
   * Close the dropdown menu by clicking outside
   */
  async close() {
    // Try clicking on the trigger first to close, then fall back to clicking outside
    try {
      await this.page.keyboard.press("Escape");
    } catch {
      // If escape doesn't work, try clicking on a safe area
      await this.page.click("html");
    }
  }

  /**
   * Get all menu items
   */
  getMenuItems() {
    return this.page.getByRole("menuitem");
  }

  /**
   * Get a specific menu item by text
   */
  getMenuItem(text: string) {
    return this.page.getByRole("menuitem", { name: text });
  }

  /**
   * Click a menu item by text
   */
  async clickMenuItem(text: string) {
    await this.getMenuItem(text).click();
  }

  /**
   * Get submenu items
   */
  getSubMenuItems(parentText: string) {
    // First hover over the parent submenu to open it
    return this.page.getByText(parentText);
  }

  /**
   * Open a submenu by hovering over it
   */
  async openSubMenu(submenuText: string) {
    await this.page.getByText(submenuText).hover();
  }

  /**
   * Get menu separators
   */
  getMenuSeparators() {
    return this.page.locator('[class*="DropdownMenuSeparator"]');
  }

  /**
   * Get the menu content container
   */
  getMenuContent() {
    return this.page.locator('[class*="DropdownMenuContent"]');
  }

  /**
   * Check if the menu is open
   */
  async isOpen() {
    try {
      const content = this.getMenuContent();
      return await content.isVisible();
    } catch {
      return false;
    }
  }

  /**
   * Wait for menu to open
   */
  async waitForOpen() {
    await this.getMenuContent().waitFor({ state: "visible" });
  }

  /**
   * Wait for menu to close
   */
  async waitForClose() {
    await this.getMenuContent().waitFor({ state: "hidden" });
  }
}

// --- Slider

export class SliderDriver extends ComponentDriver {
  private async getActiveThumb(thumbNumber: number = 0) {
    const thumbs = this.page.getByRole("slider");
    const thumbCount = await thumbs.count();
    if (thumbCount === 0) {
      throw new Error("No slider thumb found to drag");
    }
    if (thumbNumber < 0) {
      thumbNumber = 0;
    } else if (thumbNumber >= thumbCount) {
      thumbNumber = thumbCount - 1;
    }
    return thumbs.nth(thumbNumber);
  }

  async dragThumbByMouse(location: "start" | "end" | "middle", thumbNumber: number = 0) {
    const track = this.page.locator("[data-track]");
    await track.waitFor({ state: "visible" });

    const activeThumb = await this.getActiveThumb(thumbNumber);
    await activeThumb.waitFor({ state: "visible" });

    // Get the thumb's current position for relative movement
    const thumbBox = await activeThumb.boundingBox();
    if (!thumbBox) {
      throw new Error("Could not get thumb bounding box");
    }
    const trackBox = await track.boundingBox();
    if (!trackBox) {
      throw new Error("Could not get track bounding box");
    }

    // Calculate target position relative to track
    let targetX: number;
    if (location === "start") {
      targetX = trackBox.x;
    } else if (location === "end") {
      targetX = trackBox.x + trackBox.width;
    } else {
      // middle
      targetX = trackBox.x + trackBox.width / 2;
    }
    const targetY = trackBox.y + trackBox.height / 2;

    await activeThumb.hover();
    await this.page.mouse.down({ button: "left" });
    await this.page.mouse.move(targetX, targetY);
    await this.page.mouse.up();
  }

  async stepThumbByKeyboard(
    key: "ArrowLeft" | "ArrowRight" | "Home" | "End",
    thumbNumber: number = 0,
    repeat: number = 1,
  ) {
    const activeThumb = await this.getActiveThumb(thumbNumber);
    await activeThumb.focus();
    for (let i = 0; i < repeat; i++) {
      await this.page.keyboard.press(key);
    }
  }
}

```

--------------------------------------------------------------------------------
/xmlui/src/components/Stack/Stack.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { test, expect } from "../../testing/fixtures";
import { overflows, getBounds } from "../../testing/component-test-helpers";

const PAGE_WIDTH = 1280;
const PAGE_HEIGHT = 720;
test.use({ viewport: { width: PAGE_WIDTH, height: PAGE_HEIGHT } });

// =============================================================================
// BASIC FUNCTIONALITY TESTS
// =============================================================================

test.describe("Basic Functionality", () => {
  test("can render empty", async ({ page, initTestBed }) => {
    await initTestBed(`<Stack testId="stack"></Stack>`);
    await expect(page.getByTestId("stack")).toBeAttached();
    await expect(page.getByTestId("stack")).toBeEmpty();
  });
});

// =============================================================================
// LAYOUT TESTS
// =============================================================================

test.describe("Layout", () => {
  // "(horizontal) children with unspecified dimensions" -> width is content size, height is content size
  test("(horizontal) children with unspecified dimensions", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan">Text #1</Text>
        <Text testId="item_1" backgroundColor="yellow">Text #2</Text>
        <Text testId="item_2" backgroundColor="lightgreen">Text #3</Text>
      </Stack>
    `);

    const { width: stackWidth, height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const { width: itemWidth0, height: itemHeight0 } = await getBounds(page.getByTestId("item_0"));
    const { width: itemWidth1 } = await getBounds(page.getByTestId("item_1"));
    const { width: itemWidth2, right: itemRight2 } = await getBounds(page.getByTestId("item_2"));
    const itemWidthSum = itemWidth0 + itemWidth1 + itemWidth2;

    // ensure the elements are not overflowing and that the stack is not as wide as the width-sum of the elements
    // this can be stated, since we set the viewport size at the start,
    // which is bigger than the width-sum of the elements
    expect(itemWidthSum).toBeLessThan(stackWidth);

    //no gaps between elements
    // with tolerance, since we are comparing floating point number. The pixel values could be non-whole numbers
    // in which case adding up fractions could have a very small difference that would make the test fail
    expect(itemWidthSum).toEqualWithTolerance(itemRight2);

    // enusre that the stack is as tall as the tallest element (they all have the same height in this case)
    expect(stackHeight).toEqual(itemHeight0);
  });

  // "(vertical) children with unspecified dimensions" -> width fills available space, height is content size
  test("(vertical) children with unspecified dimensions, orientation is implicit", async ({
    page,
    initTestBed,
  }) => {
    await initTestBed(`
      <Stack testId="stack" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan">Text #1</Text>
        <Text testId="item_1" backgroundColor="yellow">Text #2</Text>
        <Text testId="item_2" backgroundColor="lightgreen">Text #3</Text>
      </Stack>
    `);

    const { height: stackHeight, width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const itemHeightSum = itemDims0.height + itemDims1.height + itemDims2.height;

    expect(itemHeightSum).toEqualWithTolerance(stackHeight);
    expect(itemDims0.width).toEqual(stackWidth);
    expect(itemDims1.width).toEqual(stackWidth);
    expect(itemDims2.width).toEqual(stackWidth);
  });

  // "(horizontal) block children with unspecified dimensions" -> width is content size,
  // height is content size, block children are treated as inline elements
  test("(horizontal) block children with unspecified dimensions", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Fragment>
        <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" gap="0">
          <Text testId="item_0" backgroundColor="cyan">Heading 1</Text>
          <Text testId="item_1" backgroundColor="yellow">Heading 2</Text>
          <Text testId="item_2" backgroundColor="lightgreen">Heading 3</Text>
        </Stack>
        <Stack testId="stack2" orientation="horizontal" backgroundColor="lightgray">
          <Text testId="item_3" backgroundColor="coral">Heading 1Heading 2Heading 3</Text>
        </Stack>
      </Fragment>
    `);

    const { width: stackWidth, height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const { width: itemWidth0, height: itemHeight0 } = await getBounds(page.getByTestId("item_0"));
    const { width: itemWidth1, height: itemHeight1 } = await getBounds(page.getByTestId("item_1"));
    const { width: itemWidth2, height: itemHeight2 } = await getBounds(page.getByTestId("item_2"));
    const { width: itemWidth3 } = await getBounds(page.getByTestId("item_3"));

    const itemWidthSum = itemWidth0 + itemWidth1 + itemWidth2;
    const tallestItemHeight = Math.max(itemHeight0, itemHeight1, itemHeight2);

    expect(itemWidthSum).toBeLessThan(stackWidth);
    expect(itemWidthSum).toEqualWithTolerance(itemWidth3);
    expect(stackHeight).toEqual(tallestItemHeight);
  });

  // "(horizontal) children with fixed dimensions" -> Stack does not alter dimensions
  test("(horizontal) children with fixed dimensions", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="72px" height="36px">72 x 36</Text>
        <Text testId="item_1" backgroundColor="yellow" width="144px" height="72px">144 x 72</Text>
        <Text testId="item_2" backgroundColor="lightgreen" width="64px" height="48px">64 x 48</Text>
      </Stack>
    `);

    const { height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const tallestItemHeight = 72;
    const itemWidthSum = 72 + 144 + 64;

    //no gaps between items
    expect(itemWidthSum).toEqualWithTolerance(itemDims2.right);
    expect(stackHeight).toEqual(tallestItemHeight);
    expect(itemDims0).toMatchObject({ width: 72, height: 36 });
    expect(itemDims1).toMatchObject({ width: 144, height: 72 });
    expect(itemDims2).toMatchObject({ width: 64, height: 48 });
  });

  // "(vertical) children with fixed dimensions" -> Stack does not alter dimensions
  test("(vertical) children with fixed dimensions", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="72px" height="36px">72 x 36</Text>
        <Text testId="item_1" backgroundColor="yellow" width="144px" height="72px">144 x 72</Text>
        <Text testId="item_2" backgroundColor="lightgreen" width="64px" height="48px">64 x 48</Text>
      </Stack>
    `);

    const { width: stackWidth, height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const widestItemWidth = 144;
    const itemHeightSum = 36 + 72 + 48;

    expect(widestItemWidth).toBeLessThan(stackWidth);
    expect(itemHeightSum).toEqualWithTolerance(itemDims2.bottom);
    expect(itemHeightSum).toEqualWithTolerance(stackHeight);
    expect(itemDims0).toMatchObject({ width: 72, height: 36 });
    expect(itemDims1).toMatchObject({ width: 144, height: 72 });
    expect(itemDims2).toMatchObject({ width: 64, height: 48 });
  });

  // (horizontal) children with fixed width and unspecified height -> item height is the same as stack height
  test("(horizontal) children with fixed width and unspecified height", async ({
    page,
    initTestBed,
  }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="72px">W: 72</Text>
        <Text testId="item_1" backgroundColor="yellow" width="144px">W: 144</Text>
        <Text testId="item_2" backgroundColor="lightgreen" width="48px">W: 48 + long, long, long text</Text>
      </Stack>
    `);

    const { height: stackHeight, width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const itemWidthSum = itemDims0.width + itemDims1.width + itemDims2.width;

    expect(itemWidthSum).toEqualWithTolerance(itemDims2.right);
    expect(itemWidthSum).toBeLessThan(stackWidth);

    expect(itemDims0.height).toEqual(stackHeight);
    expect(itemDims1.height).toEqual(stackHeight);
    expect(itemDims2.height).toEqual(stackHeight);

    expect(itemDims0.width).toEqual(72);
    expect(itemDims1.width).toEqual(144);
    expect(itemDims2.width).toEqual(48);
  });

  // (vertical) children with fixed height and unspecified width -> width fills available space
  test("(vertical) children with fixed height and unspecified width ", async ({
    page,
    initTestBed,
  }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" gap="0">
        <Text testId="item_0" backgroundColor="cyan" height="36px">H: 36</Text>
        <Text testId="item_1" backgroundColor="yellow" height="72px">H: 72</Text>
        <Text testId="item_2" backgroundColor="lightgreen" height="48px">H: 48 + long, long, long text</Text>
      </Stack>
    `);

    const { height: stackHeight, width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const itemHeightSum = 36 + 72 + 48;

    expect(itemHeightSum).toEqualWithTolerance(itemDims2.bottom);
    expect(itemHeightSum).toEqualWithTolerance(stackHeight);

    expect(itemDims0.width).toEqual(stackWidth);
    expect(itemDims1.width).toEqual(stackWidth);
    expect(itemDims2.width).toEqual(stackWidth);

    expect(itemDims0.height).toEqual(36);
    expect(itemDims1.height).toEqual(72);
    expect(itemDims2.height).toEqual(48);
  });

  // (horizontal) children with fixed height and unspecified width -> item widths are conent size, item heights are not altered
  test("(horizontal) children with fixed height and unspecified width", async ({
    page,
    initTestBed,
  }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="0">
        <Text testId="item_0" backgroundColor="cyan" height="36px">H: 36</Text>
        <Text testId="item_1" backgroundColor="yellow" height="72px">H: 72</Text>
        <Text testId="item_2" backgroundColor="lightgreen" height="48px">H: 48</Text>
      </Stack>
    `);

    const { height: stackHeight, width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const itemWidthSum = itemDims0.width + itemDims1.width + itemDims2.width;
    const tallestItemHeight = 72;

    expect(itemWidthSum).toBeLessThan(stackWidth);
    expect(stackHeight).toEqual(tallestItemHeight);

    expect(itemDims0.height).toEqual(36);
    expect(itemDims1.height).toEqual(72);
    expect(itemDims2.height).toEqual(48);
  });

  // (vertical) children with fixed width and unspecified height -> item heights are content size, widths are not altered
  test("(vertical) children with fixed width and unspecified height", async ({
    page,
    initTestBed,
  }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="72px">W: 72</Text>
        <Text testId="item_1" backgroundColor="yellow" width="144px">W: 144</Text>
        <Text testId="item_2" backgroundColor="lightgreen" width="48px">W: 48 + long, long, long text</Text>
      </Stack>
    `);

    const { height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    const itemHeightSum = itemDims0.height + itemDims1.height + itemDims2.height;

    expect(itemHeightSum).toEqualWithTolerance(itemDims2.bottom);
    expect(itemHeightSum).toEqualWithTolerance(stackHeight);

    expect(itemDims0.width).toEqual(72);
    expect(itemDims1.width).toEqual(144);
    expect(itemDims2.width).toEqual(48);
  });

  test("(horizontal) percentage sizing", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="20%">W: 20%</Text>
        <H2 testId="item_1" backgroundColor="yellow" width="50%">W: 50%</H2>
        <H5 testId="item_2" backgroundColor="lightgreen" width="20%">W: 20% + long, long, long text</H5>
      </Stack>
    `);

    const { width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const { width: itemWidth0 } = await getBounds(page.getByTestId("item_0"));
    const { width: itemWidth1 } = await getBounds(page.getByTestId("item_1"));
    const { width: itemWidth2, right: lastItemRight } = await getBounds(page.getByTestId("item_2"));
    const itemWidthSum = itemWidth0 + itemWidth1 + itemWidth2;

    expect(stackWidth).toEqual(PAGE_WIDTH);
    expect(itemWidthSum).toEqualWithTolerance(lastItemRight);
    expect(itemWidth0).toEqualWithTolerance(0.2 * stackWidth);
    expect(itemWidth1).toEqualWithTolerance(0.5 * stackWidth);
    expect(itemWidth2).toEqualWithTolerance(0.2 * stackWidth);
  });

  test("(vertical) percentage sizing", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" height="180px" orientation="vertical" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" height="20%">W: 20%</Text>
        <Text testId="item_1" backgroundColor="yellow" height="50%">W: 50%</Text>
        <Text testId="item_2" backgroundColor="lightgreen" height="20%">W: 20% + long, long, long text</Text>
      </Stack>
    `);

    const { height: stackHeight, width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const { height: itemHeight0, width: itemWidth0 } = await getBounds(page.getByTestId("item_0"));
    const { height: itemHeight1 } = await getBounds(page.getByTestId("item_1"));
    const { height: itemHeight2, bottom: lastItemBottom } = await getBounds(
      page.getByTestId("item_2"),
    );
    const itemHeightSum = itemHeight0 + itemHeight1 + itemHeight2;

    expect(itemWidth0).toEqual(stackWidth);
    expect(itemHeightSum).toEqualWithTolerance(lastItemBottom);
    expect(itemHeight0).toEqualWithTolerance(0.2 * stackHeight);
    expect(itemHeight1).toEqualWithTolerance(0.5 * stackHeight);
    expect(itemHeight2).toEqualWithTolerance(0.2 * stackHeight);
  });

  test("(horizontal) percentage sizing fully filled", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="20%">W: 20%</Text>
        <H2 testId="item_1" backgroundColor="yellow" width="50%">W: 50%</H2>
        <H5 testId="item_2" backgroundColor="lightgreen" width="30%">W: 30% + long, long, long text</H5>
      </Stack>
    `);
    const { width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const { right: lastItemRight } = await getBounds(page.getByTestId("item_2"));
    expect(stackWidth).toEqualWithTolerance(lastItemRight);
  });

  test("(vertical) percentage sizing fully filled", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" height="180px" orientation="vertical" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" height="20%">W: 20%</Text>
        <Text testId="item_1" backgroundColor="yellow" height="50%">W: 50%</Text>
        <Text testId="item_2" backgroundColor="lightgreen" height="30%">W: 30% + long, long, long text</Text>
      </Stack>
    `);
    const { height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const { bottom: lastItemBottom } = await getBounds(page.getByTestId("item_2"));
    expect(stackHeight).toEqualWithTolerance(lastItemBottom);
  });

  // (horizontal) percentage overflow X
  test("(horizontal) percentage overflow X", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="30%">W: 30%</Text>
        <H2 testId="item_1" backgroundColor="yellow" width="50%">W: 50%</H2>
        <H5 testId="item_2" backgroundColor="lightgreen" width="40%">W: 40% + long, long, long text</H5>
      </Stack>
    `);
    const isOverflown = await overflows(page.getByTestId("stack"), "x");
    expect(isOverflown).toEqual(true);
  });

  // (vertical) percentage overflow Y
  test("(vertical) percentage overflow Y", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" height="180px" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" height="30%">H: 30%</Text>
        <H2 testId="item_1" backgroundColor="yellow" height="60%">H: 60%</H2>
        <H5 testId="item_2" backgroundColor="lightgreen" height="20%">H: 20% + long, long, long text</H5>
      </Stack>
    `);
    const isOverflown = await overflows(page.getByTestId("stack"), "y");
    expect(isOverflown).toEqual(true);
  });

  test("(horizontal) star sizing", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" width="100%" orientation="horizontal" backgroundColor="lightgray" gap="0">
        <Text testId="item_0" backgroundColor="cyan" width="100px">W: 100</Text>
        <Text testId="item_1" backgroundColor="yellow" width="3*">W: 3*</Text>
        <Text testId="item_2" backgroundColor="lightgreen" width="*">W: *</Text>
      </Stack>
    `);
    const { width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const { width: itemWidth0 } = await getBounds(page.getByTestId("item_0"));
    const { width: itemWidth1 } = await getBounds(page.getByTestId("item_1"));
    const { width: itemWidth2, right: lastItemRight } = await getBounds(page.getByTestId("item_2"));
    const itemWidthSum = itemWidth0 + itemWidth1 + itemWidth2;

    const expectedItemWidth0 = 100;
    const expectedItemWidth1 = (stackWidth - 100) * (3 / 4);
    const expectedItemWidth2 = (stackWidth - 100) * (1 / 4);

    expect(itemWidthSum).toEqualWithTolerance(lastItemRight);
    expect(itemWidth0).toEqual(expectedItemWidth0);
    expect(itemWidth1).toEqualWithTolerance(expectedItemWidth1);
    expect(itemWidth2).toEqualWithTolerance(expectedItemWidth2);
  });

  // (horizontal) star sizing comparison -> Larger Stack have larger star sized children, px sizes are same
  test("(horizontal) star sizing comparison", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Fragment>
        <Stack testId="stack_0" orientation="horizontal" width="600px" gap="0">
          <Text testId="ref_item_0" backgroundColor="cyan" width="100px">W: 100</Text>
          <Text testId="ref_item_1" backgroundColor="yellow" width="3*">W: 3*</Text>
          <Text testId="ref_item_2" backgroundColor="lightgreen" width="*">W: *</Text>
        </Stack>
        <Stack testId="stack_1" orientation="horizontal" width="300px" gap="0">
          <Text testId="item_0" backgroundColor="cyan" width="100px">W: 100</Text>
          <Text testId="item_1" backgroundColor="yellow" width="3*">W: 3*</Text>
          <Text testId="item_2" backgroundColor="lightgreen" width="*">W: *</Text>
        </Stack>
      </Fragment>
    `);
    const { width: refItemWidth0 } = await getBounds(page.getByTestId("ref_item_0"));
    const { width: refItemWidth1 } = await getBounds(page.getByTestId("ref_item_1"));
    const { width: refItemWidth2 } = await getBounds(page.getByTestId("ref_item_2"));
    const { width: itemWidth0 } = await getBounds(page.getByTestId("item_0"));
    const { width: itemWidth1 } = await getBounds(page.getByTestId("item_1"));
    const { width: itemWidth2 } = await getBounds(page.getByTestId("item_2"));

    expect(refItemWidth0).toEqual(itemWidth0);
    expect(refItemWidth1).toBeGreaterThan(itemWidth1);
    expect(refItemWidth2).toBeGreaterThan(itemWidth2);
  });

  // (vertical) star sizing comparison -> Larger Stack have larger star sized children, px sizes are same
  test("(vertical) star sizing comparison", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Fragment>
        <Stack testId="stack_0" orientation="vertical" height="600px" gap="0">
          <Text testId="ref_item_0" backgroundColor="cyan" height="100px">W: 100</Text>
          <Text testId="ref_item_1" backgroundColor="yellow" height="3*">W: 3*</Text>
          <Text testId="ref_item_2" backgroundColor="lightgreen" height="*">W: *</Text>
        </Stack>
        <Stack testId="stack_1" orientation="vertical" height="300px" gap="0">
          <Text testId="item_0" backgroundColor="cyan" height="100px">W: 100</Text>
          <Text testId="item_1" backgroundColor="yellow" height="3*">W: 3*</Text>
          <Text testId="item_2" backgroundColor="lightgreen" height="*">W: *</Text>
        </Stack>
      </Fragment>
    `);
    const { height: refItemHeight0 } = await getBounds(page.getByTestId("ref_item_0"));
    const { height: refItemHeight1 } = await getBounds(page.getByTestId("ref_item_1"));
    const { height: refItemHeight2 } = await getBounds(page.getByTestId("ref_item_2"));
    const { height: itemHeight0 } = await getBounds(page.getByTestId("item_0"));
    const { height: itemHeight1 } = await getBounds(page.getByTestId("item_1"));
    const { height: itemHeight2 } = await getBounds(page.getByTestId("item_2"));

    expect(refItemHeight0).toEqual(itemHeight0);
    expect(refItemHeight1).toBeGreaterThan(itemHeight1);
    expect(refItemHeight2).toBeGreaterThan(itemHeight2);
  });

  // (horizontal) gaps + percentage -> children use available space
  test("(horizontal) paddings + percentage", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" gap="0"
        paddingHorizontal="50px" paddingVertical="50px">
        <Text testId="item_0" backgroundColor="cyan" width="25%">W: 25%</Text>
        <Text testId="item_1" backgroundColor="yellow" width="50%">W: 50%</Text>
        <Text testId="item_2" backgroundColor="lightgreen" width="25%">W: 25%</Text>
      </Stack>
    `);
    const { width: stackWidth } = await getBounds(page.getByTestId("stack"));
    const { width: itemWidth0, left: firstItemLeft } = await getBounds(page.getByTestId("item_0"));
    const { width: itemWidth1 } = await getBounds(page.getByTestId("item_1"));
    const { width: itemWidth2, right: lastItemRight } = await getBounds(page.getByTestId("item_2"));

    const itemWidthSum = itemWidth0 + itemWidth1 + itemWidth2;
    expect(firstItemLeft).toEqual(50);
    expect(lastItemRight).toEqual(stackWidth - 50);
    expect(itemWidthSum).toEqualWithTolerance(stackWidth - 2 * 50);
  });

  // (vertical) gaps + percentage -> children use available space
  test("(vertical) paddings + percentage", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" backgroundColor="lightgray" gap="0"
        height="240px" paddingHorizontal="50px" paddingVertical="50px">
        <Text testId="item_0" backgroundColor="cyan" height="25%">H: 25%</Text>
        <Text testId="item_1" backgroundColor="yellow" height="50%">H: 50%</Text>
        <Text testId="item_2" backgroundColor="lightgreen" height="25%">H: 25%</Text>
      </Stack>
    `);
    const { height: stackHeight } = await getBounds(page.getByTestId("stack"));
    const { top: firstItemTop } = await getBounds(page.getByTestId("item_0"));
    const { bottom: lastItemBottom } = await getBounds(page.getByTestId("item_2"));

    expect(firstItemTop).toEqual(50);
    expect(lastItemBottom).toEqual(stackHeight - 50);
  });

  test("(horizontal) gaps", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack orientation="horizontal" backgroundColor="lightgray" gap="50px">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);

    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    expect(itemDims0.left).toEqual(0);
    expect(itemDims1.left).toEqual(itemDims0.right + 50);
    expect(itemDims2.left).toEqual(2 * (itemDims0.right + 50));
  });

  test("(vertical) gaps", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack orientation="vertical" backgroundColor="lightgray" gap="50px">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);

    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    expect(itemDims0.top).toEqual(0);
    expect(itemDims1.top).toEqual(itemDims0.bottom + 50);
    expect(itemDims2.top).toEqual(2 * (itemDims0.bottom + 50));
  });

  // (horizontal) gaps + percentage -> gaps push the content out of the container
  test("(horizontal) gaps + percentage", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" padding="1rem" gap="2rem">
        <Stack backgroundColor="red" height="36px" width="25%" />
        <Stack backgroundColor="green" height="36px" width="50%" />
        <Stack backgroundColor="blue" height="36px" width="25%" />
      </Stack>
    `);
    const isOverflown = await overflows(page.getByTestId("stack"), "x");
    expect(isOverflown).toEqual(true);
  });

  // (horizontal) gaps + star sizing -> gaps don't push the content out of the container
  test("(horizontal) gaps + star sizing", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" backgroundColor="lightgray" padding="1rem" gap="2rem">
        <Stack backgroundColor="red" height="36px" width="*" />
        <Stack backgroundColor="green" height="36px" width="*" />
        <Stack backgroundColor="blue" height="36px" width="*" />
        <Stack backgroundColor="purple" height="36px" width="*" />
      </Stack>
    `);
    const isOverflown = await overflows(page.getByTestId("stack"), "x");
    expect(isOverflown).toEqual(false);
  });

  test("(horizontal) rtl rendering direction", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="1rem" direction="rtl">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);

    const { right: stackRight } = await getBounds(page.getByTestId("stack"));
    const { right: itemRight0, left: itemLeft0 } = await getBounds(page.getByTestId("item_0"));
    const { left: itemLeft1 } = await getBounds(page.getByTestId("item_1"));
    const { left: itemLeft2 } = await getBounds(page.getByTestId("item_2"));

    expect(itemLeft2).toBeLessThan(itemLeft1);
    expect(itemLeft1).toBeLessThan(itemLeft0);
    expect(itemRight0).toEqual(stackRight);
  });

  test("(horizontal) reverse rendering direction", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="1rem" reverse="true">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);
    const { right: stackRight } = await getBounds(page.getByTestId("stack"));
    const { right: itemRight0, left: itemLeft0 } = await getBounds(page.getByTestId("item_0"));
    const { left: itemLeft1 } = await getBounds(page.getByTestId("item_1"));
    const { left: itemLeft2 } = await getBounds(page.getByTestId("item_2"));

    expect(itemLeft2).toBeLessThan(itemLeft1);
    expect(itemLeft1).toBeLessThan(itemLeft0);
    expect(itemRight0).toEqual(stackRight);
  });

  test("(vertical) reverse rendering direction", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" gap="1rem" reverse="true">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);
    const { bottom: stackBottom } = await getBounds(page.getByTestId("stack"));
    const { bottom: itemBottom0, top: itemTop0 } = await getBounds(page.getByTestId("item_0"));
    const { top: itemTop1 } = await getBounds(page.getByTestId("item_1"));
    const { top: itemTop2 } = await getBounds(page.getByTestId("item_2"));

    expect(itemTop2).toBeLessThan(itemTop1);
    expect(itemTop1).toBeLessThan(itemTop0);
    expect(itemBottom0).toEqual(stackBottom);
  });

  test("(horizontal) content wrapping", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="1rem" wrapContent="true">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="25%" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="40%" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="20%" />
        <Stack testId="item_3" backgroundColor="purple" height="36px" width="30%" />
      </Stack>
    `);
    const { right: stackRight, bottom: stackBottom } = await getBounds(page.getByTestId("stack"));
    const { left: itemLeft0, bottom: itemBottom0 } = await getBounds(page.getByTestId("item_0"));
    const { left: itemLeft1 } = await getBounds(page.getByTestId("item_1"));
    const { left: itemLeft2, right: itemRight2 } = await getBounds(page.getByTestId("item_2"));
    const {
      left: itemLeft3,
      bottom: itemBottom3,
      top: itemTop3,
    } = await getBounds(page.getByTestId("item_3"));

    expect(itemLeft0).toBeLessThan(itemLeft1);
    expect(itemLeft1).toBeLessThan(itemLeft2);
    expect(itemRight2).toBeLessThan(stackRight);

    expect(itemBottom0).toBeLessThan(itemTop3);
    expect(itemLeft3).toEqual(itemLeft0);
    expect(itemBottom3).toEqual(stackBottom);
  });

  test("(horizontal) horizontalAlignment center", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" horizontalAlignment="center" gap="0">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);
    const { right: stackRight } = await getBounds(page.getByTestId("stack"));
    const { left: itemLeft0 } = await getBounds(page.getByTestId("item_0"));
    const { right: itemRight2 } = await getBounds(page.getByTestId("item_2"));
    const itemWidthSum = 3 * 36;

    expect(itemLeft0).toEqual(stackRight / 2 - itemWidthSum / 2);
    expect(itemRight2).toEqual(stackRight / 2 + itemWidthSum / 2);
  });

  test("(vertical) horizontal alignment end", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="vertical" horizontalAlignment="end" gap="0">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="36px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="36px" width="36px" />
      </Stack>
    `);
    const { right: stackRight } = await getBounds(page.getByTestId("stack"));
    const { right: itemRight0 } = await getBounds(page.getByTestId("item_0"));
    const { right: itemRight1 } = await getBounds(page.getByTestId("item_1"));
    const { right: itemRight2 } = await getBounds(page.getByTestId("item_2"));

    expect(itemRight0).toEqual(stackRight);
    expect(itemRight1).toEqual(stackRight);
    expect(itemRight2).toEqual(stackRight);
  });

  test("(horizontal) verticalAlignment center", async ({ page, initTestBed }) => {
    await initTestBed(`
      <Stack testId="stack" orientation="horizontal" gap="1rem" verticalAlignment="center">
        <Stack testId="item_0" backgroundColor="red" height="36px" width="36px" />
        <Stack testId="item_1" backgroundColor="green" height="72px" width="36px" />
        <Stack testId="item_2" backgroundColor="blue" height="48px" width="36px" />
      </Stack>
    `);
    const stackDims = await getBounds(page.getByTestId("stack"));
    const itemDims0 = await getBounds(page.getByTestId("item_0"));
    const itemDims1 = await getBounds(page.getByTestId("item_1"));
    const itemDims2 = await getBounds(page.getByTestId("item_2"));

    expect(itemDims0.top).toEqual(stackDims.height / 2 - itemDims0.height / 2);
    expect(itemDims0.bottom).toEqual(stackDims.height / 2 + itemDims0.height / 2);

    expect(itemDims1.top).toEqual(stackDims.top);
    expect(itemDims1.bottom).toEqual(stackDims.bottom);

    expect(itemDims2.top).toEqual(stackDims.height / 2 - itemDims2.height / 2);
    expect(itemDims2.bottom).toEqual(stackDims.height / 2 + itemDims2.height / 2);
  });

  test(`overflowY="scroll"`, async ({ page, initTestBed }) => {
    await initTestBed(`
      <VStack testId="stack" width="100px" height="60px" backgroundColor="cyan"
        overflowY="scroll">
        <Text testId="item0">
          As its container width and height are fixed, this long text does not
          fit into it; it will overflow.
        </Text>
      </VStack>
    `);
    const { scrollHeight, clientHeight } = await page
      .getByTestId("stack")
      .evaluate((elem) => ({ scrollHeight: elem.scrollHeight, clientHeight: elem.clientHeight }));

    expect(scrollHeight).toBeGreaterThan(clientHeight);
  });

  test(`overflowX="scroll"`, async ({ page, initTestBed }) => {
    await initTestBed(`
      <HStack testId="stack" width="100px" height="60px" backgroundColor="cyan"
        overflowX="scroll">
        <Text testId="item0">
          As its container width and height are fixed, this long text does not
          fit into it; it will overflow.
        </Text>
      </HStack>
    `);
    const { scrollWidth, clientWidth } = await page
      .getByTestId("stack")
      .evaluate((elem) => ({ scrollWidth: elem.scrollWidth, clientWidth: elem.clientWidth }));

    expect(scrollWidth).toBeGreaterThan(clientWidth);
  });

  // When you set overflowX to scroll, it will automatically set overflowY to scroll if the text exceeds the viewport vertically
  test(`overflowX sets overflowY`, async ({ page, initTestBed }) => {
    await initTestBed(`
      <VStack testId="stack" height="50px" width="300px" backgroundColor="cyan"
        overflowX="scroll">
        <Text width="400px" backgroundColor="silver" opacity="0.8">
          This text sets its size explicitly bigger than its container.
          As it does not fit into the container's viewport, it overflows.
          However, its container supports horizontal scrolling so you can
          see its content.
        </Text>
      </VStack>
    `);
    const { scrollHeight, clientHeight, scrollWidth, clientWidth } = await page
      .getByTestId("stack")
      .evaluate((elem) => ({
        scrollHeight: elem.scrollHeight,
        clientHeight: elem.clientHeight,
        scrollWidth: elem.scrollWidth,
        clientWidth: elem.clientWidth,
      }));

    expect(scrollWidth).toBeGreaterThan(clientWidth);
    expect(scrollHeight).toBeGreaterThan(clientHeight);
  });
});

```
Page 97/143FirstPrevNextLast