#
tokens: 48931/50000 4/1634 files (page 79/189)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 79 of 189. Use http://codebase.md/xmlui-org/xmlui/xmlui/tools/vscode/resources/xmlui-markup-syntax-highlighting.png?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   ├── config.json
│   ├── cyan-tools-design.md
│   ├── every-moments-teach.md
│   ├── fancy-laws-drop.md
│   ├── full-symbols-accept.md
│   └── tricky-zoos-crash.md
├── .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-swa.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
│   │           ├── HelloMd.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.module.scss
│   │       │   ├── 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.tsx
│   │       │   ├── StandalonePlayground.tsx
│   │       │   ├── StandalonePlaygroundNative.module.scss
│   │       │   ├── StandalonePlaygroundNative.tsx
│   │       │   ├── ThemeSwitcher.module.scss
│   │       │   ├── ThemeSwitcher.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
    │   │   │   └── test-padding.xmlui
    │   │   ├── 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.json
    ├── tsdown.config.ts
    ├── vite.config.ts
    └── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/xmlui/src/components/Charts/Tooltip/TooltipContent.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { expect, test } from "../../../testing/fixtures";
  2 | 
  3 | // Test data helpers - using proper XMLUI data format
  4 | const sampleData = `[
  5 |   { name: 'Desktop', value: 400, fill: '#8884d8' },
  6 |   { name: 'Mobile', value: 300, fill: '#82ca9d' },
  7 |   { name: 'Tablet', value: 200, fill: '#ffc658' },
  8 |   { name: 'Other', value: 100, fill: '#ff7300' }
  9 | ]`;
 10 | 
 11 | const multiSeriesData = `[
 12 |   { name: 'Jan', sales: 100, profit: 20, expenses: 80 },
 13 |   { name: 'Feb', sales: 150, profit: 30, expenses: 120 },
 14 |   { name: 'Mar', sales: 120, profit: 25, expenses: 95 },
 15 |   { name: 'Apr', sales: 180, profit: 40, expenses: 140 }
 16 | ]`;
 17 | 
 18 | const largeValueData = `[
 19 |   { name: 'Category A', value: 1234567 },
 20 |   { name: 'Category B', value: 9876543 },
 21 |   { name: 'Category C', value: 5555555 }
 22 | ]`;
 23 | 
 24 | // Chart selectors - Tooltip specific
 25 | const chartRoot = ".recharts-responsive-container";
 26 | const tooltipSelector = ".recharts-tooltip-wrapper";
 27 | const tooltipContentSelector = "[class*='tooltipContainer']";
 28 | const tooltipLabelSelector = ".label";
 29 | const tooltipIndicatorSelector = "[class*='indicator']";
 30 | const tooltipValueSelector = "[class*='valueText']";
 31 | const tooltipNameSelector = "[class*='mutedText']";
 32 | 
 33 | // =============================================================================
 34 | // BASIC FUNCTIONALITY TESTS
 35 | // =============================================================================
 36 | 
 37 | test.describe("Basic Functionality", () => {
 38 |   // Smoke tests
 39 |   test.describe("smoke tests", { tag: "@smoke" }, () => {
 40 |     test("TooltipContent renders in PieChart on hover", async ({ initTestBed, page }) => {
 41 |       await initTestBed(`
 42 |         <PieChart
 43 |           nameKey="name"
 44 |           dataKey="value"
 45 |           data="{${sampleData}}"
 46 |           width="400px"
 47 |           height="400px"
 48 |         />
 49 |       `);
 50 | 
 51 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
 52 | 
 53 |       // Hover over a pie sector to trigger tooltip
 54 |       const pieSector = page.locator(".recharts-pie-sector").first();
 55 |       await pieSector.hover();
 56 | 
 57 |       // Tooltip should appear
 58 |       await expect(page.locator(tooltipSelector)).toBeVisible();
 59 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
 60 |     });
 61 | 
 62 |     test("TooltipContent renders in BarChart on hover", async ({ initTestBed, page }) => {
 63 |       await initTestBed(`
 64 |         <BarChart
 65 |           xKey="name"
 66 |           yKeys="{['value']}"
 67 |           data="{${sampleData}}"
 68 |           width="400px"
 69 |           height="400px"
 70 |         />
 71 |       `);
 72 | 
 73 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
 74 | 
 75 |       // Hover over a bar to trigger tooltip
 76 |       const barElement = page.locator(".recharts-bar-rectangle").first();
 77 |       await barElement.hover();
 78 | 
 79 |       // Check if tooltip content is visible
 80 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
 81 |       await expect(page.locator(tooltipNameSelector)).toBeVisible();
 82 |       await expect(page.locator(tooltipValueSelector)).toBeVisible();
 83 |     });
 84 |   });
 85 | 
 86 |   // Indicator prop tests
 87 |   test.describe("indicator prop", () => {
 88 |     test("displays dot indicator by default", async ({ initTestBed, page }) => {
 89 |       await initTestBed(`
 90 |         <PieChart
 91 |           nameKey="name"
 92 |           dataKey="value"
 93 |           data="{${sampleData}}"
 94 |           width="400px"
 95 |           height="400px"
 96 |         />
 97 |       `);
 98 | 
 99 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
100 |       const pieSector = page.locator(".recharts-pie-sector").first();
101 |       await pieSector.hover();
102 | 
103 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
104 |       const indicator = page.locator(tooltipIndicatorSelector);
105 |       await expect(indicator).toBeVisible();
106 | 
107 |       // Should have dot styling (default)
108 |       await expect(indicator).toHaveClass(/dot/);
109 |     });
110 | 
111 |     test("displays line indicator when specified", async ({ initTestBed, page }) => {
112 |       // Note: Since TooltipContent is used internally, we test through chart context
113 |       // The indicator prop would be passed through chart tooltip configuration
114 |       await initTestBed(`
115 |         <BarChart
116 |           xKey="name"
117 |           yKeys="{['value']}"
118 |           data="{${sampleData}}"
119 |           width="400px"
120 |           height="400px"
121 |         />
122 |       `);
123 | 
124 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
125 | 
126 |       // Hover over a bar to trigger tooltip
127 |       const barElement = page.locator(".recharts-bar-rectangle").first();
128 |       await barElement.hover();
129 | 
130 |       // Check if tooltip content is visible with proper styling
131 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
132 |       await expect(page.locator(tooltipNameSelector)).toBeVisible();
133 |       await expect(page.locator(tooltipValueSelector)).toBeVisible();
134 |     });
135 |   });
136 | 
137 |   // Data handling tests
138 |   test.describe("data handling", () => {
139 |     test("displays single data point correctly", async ({ initTestBed, page }) => {
140 |       const singleData = `[{ name: 'Single', value: 100 }]`;
141 | 
142 |       await initTestBed(`
143 |         <PieChart
144 |           nameKey="name"
145 |           dataKey="value"
146 |           data="{${singleData}}"
147 |           width="400px"
148 |           height="400px"
149 |         />
150 |       `);
151 | 
152 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
153 |       const pieSector = page.locator(".recharts-pie-sector").first();
154 |       await pieSector.hover();
155 | 
156 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
157 |       await expect(page.locator(tooltipNameSelector)).toContainText("Single");
158 |       await expect(page.locator(tooltipValueSelector)).toContainText("100");
159 |     });
160 | 
161 |     test("displays multiple data series correctly", async ({ initTestBed, page }) => {
162 |       await initTestBed(`
163 |         <BarChart
164 |           xKey="name"
165 |           yKeys="{['sales', 'profit']}"
166 |           data="{${multiSeriesData}}"
167 |           width="400px"
168 |           height="400px"
169 |         />
170 |       `);
171 | 
172 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
173 | 
174 |       // Hover over a bar to trigger tooltip
175 |       const barElement = page.locator(".recharts-bar-rectangle").first();
176 |       await barElement.hover();
177 | 
178 |       // Check if tooltip shows multiple series data
179 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
180 | 
181 |       // Should show data for both series - check for multiple indicators
182 |       const indicators = page.locator(tooltipIndicatorSelector);
183 |       await expect(indicators).toHaveCount(2);
184 |     });
185 | 
186 |     test("formats large numbers with locale string", async ({ initTestBed, page }) => {
187 |       await initTestBed(`
188 |         <PieChart
189 |           nameKey="name"
190 |           dataKey="value"
191 |           data="{${largeValueData}}"
192 |           width="400px"
193 |           height="400px"
194 |         />
195 |       `);
196 | 
197 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
198 |       const pieSector = page.locator(".recharts-pie-sector").first();
199 |       await pieSector.hover();
200 | 
201 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
202 | 
203 |       // Should format large numbers with commas
204 |       const valueText = await page.locator(tooltipValueSelector).textContent();
205 |       expect(valueText).toMatch(/1,234,567/);
206 |     });
207 | 
208 |     test("handles zero values correctly", async ({ initTestBed, page }) => {
209 |       const zeroData = `[
210 |         { name: 'Zero', value: 0 },
211 |         { name: 'Positive', value: 100 }
212 |       ]`;
213 | 
214 |       await initTestBed(`
215 |         <PieChart
216 |           nameKey="name"
217 |           dataKey="value"
218 |           data="{${zeroData}}"
219 |           width="400px"
220 |           height="400px"
221 |         />
222 |       `);
223 | 
224 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
225 |       const pieSector = page.locator(".recharts-pie-sector").first();
226 |       await pieSector.hover();
227 | 
228 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
229 |       // Should handle zero values gracefully
230 |       const valueElements = page.locator(tooltipValueSelector);
231 |       await expect(valueElements.first()).toBeVisible();
232 |     });
233 |   });
234 | 
235 |   // Label handling tests
236 |   test.describe("label handling", () => {
237 |     test("displays tooltip content without label by default", async ({ initTestBed, page }) => {
238 |       await initTestBed(`
239 |         <PieChart
240 |           nameKey="name"
241 |           dataKey="value"
242 |           data="{${sampleData}}"
243 |           width="400px"
244 |           height="400px"
245 |         />
246 |       `);
247 | 
248 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
249 |       const pieSector = page.locator(".recharts-pie-sector").first();
250 |       await pieSector.hover();
251 | 
252 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
253 | 
254 |       // Default PieChart tooltip doesn't have a label, so check for name and value instead
255 |       await expect(page.locator(tooltipNameSelector)).toBeVisible();
256 |       await expect(page.locator(tooltipValueSelector)).toBeVisible();
257 |     });
258 | 
259 |     test("handles Unicode characters in labels", async ({ initTestBed, page }) => {
260 |       const unicodeData = `[
261 |         { name: 'Desktop 🖥️', value: 400 },
262 |         { name: 'Mobile 📱', value: 300 }
263 |       ]`;
264 | 
265 |       await initTestBed(`
266 |         <PieChart
267 |           nameKey="name"
268 |           dataKey="value"
269 |           data="{${unicodeData}}"
270 |           width="400px"
271 |           height="400px"
272 |         />
273 |       `);
274 | 
275 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
276 |       const pieSector = page.locator(".recharts-pie-sector").first();
277 |       await pieSector.hover();
278 | 
279 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
280 | 
281 |       // Should display Unicode characters correctly
282 |       const nameText = page.locator(tooltipNameSelector);
283 |       await expect(nameText).toContainText("🖥️");
284 |     });
285 |   });
286 | 
287 |   // Color and styling tests
288 |   test.describe("color and styling", () => {
289 |     test("displays indicator with correct colors", async ({ initTestBed, page }) => {
290 |       await initTestBed(`
291 |         <PieChart
292 |           nameKey="name"
293 |           dataKey="value"
294 |           data="{${sampleData}}"
295 |           width="400px"
296 |           height="400px"
297 |         />
298 |       `);
299 | 
300 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
301 |       const pieSector = page.locator(".recharts-pie-sector").first();
302 |       await pieSector.hover();
303 | 
304 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
305 | 
306 |       const indicator = page.locator(tooltipIndicatorSelector);
307 |       await expect(indicator).toBeVisible();
308 | 
309 |       // Indicator should have background color
310 |       const backgroundColor = await indicator.evaluate(
311 |         (el) => window.getComputedStyle(el).backgroundColor,
312 |       );
313 |       expect(backgroundColor).not.toBe("rgba(0, 0, 0, 0)"); // Not transparent
314 |     });
315 | 
316 |     test("applies consistent styling across different chart types", async ({
317 |       initTestBed,
318 |       page,
319 |     }) => {
320 |       // Test in LineChart context
321 |       await initTestBed(`
322 |         <LineChart
323 |           xKey="name"
324 |           yKeys="{['value']}"
325 |           data="{${sampleData}}"
326 |           width="400px"
327 |           height="400px"
328 |         />
329 |       `);
330 | 
331 |       await page.waitForSelector(chartRoot, { timeout: 10000 });
332 | 
333 |       // Hover over chart area to trigger tooltip (Recharts tooltip activates on chart area, not just line)
334 |       const chartSvg = page.locator(".recharts-surface").first();
335 |       await chartSvg.hover({ position: { x: 200, y: 200 } });
336 | 
337 |       // Wait for tooltip to appear
338 |       await page.waitForTimeout(500);
339 |       await expect(page.locator(tooltipContentSelector)).toBeVisible();
340 | 
341 |       // Should have consistent styling
342 |       const indicator = page.locator(tooltipIndicatorSelector);
343 |       await expect(indicator).toBeVisible();
344 |     });
345 |   });
346 | });
347 | 
348 | // =============================================================================
349 | // ACCESSIBILITY TESTS
350 | // =============================================================================
351 | 
352 | test.describe("Accessibility", () => {
353 |   test("tooltip content is accessible to screen readers", async ({ initTestBed, page }) => {
354 |     await initTestBed(`
355 |       <PieChart
356 |         nameKey="name"
357 |         dataKey="value"
358 |         data="{${sampleData}}"
359 |         width="400px"
360 |         height="400px"
361 |       />
362 |     `);
363 | 
364 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
365 |     const pieSector = page.locator(".recharts-pie-sector").first();
366 |     await pieSector.hover();
367 | 
368 |     await expect(page.locator(tooltipContentSelector)).toBeVisible();
369 | 
370 |     // Tooltip content should be accessible
371 |     const tooltipContent = page.locator(tooltipContentSelector);
372 |     await expect(tooltipContent).toBeVisible();
373 | 
374 |     // Text content should be readable
375 |     const nameText = page.locator(tooltipNameSelector);
376 |     const valueText = page.locator(tooltipValueSelector);
377 |     await expect(nameText).toBeVisible();
378 |     await expect(valueText).toBeVisible();
379 |   });
380 | 
381 |   test("tooltip has proper contrast and readability", async ({ initTestBed, page }) => {
382 |     await initTestBed(`
383 |       <PieChart
384 |         nameKey="name"
385 |         dataKey="value"
386 |         data="{${sampleData}}"
387 |         width="400px"
388 |         height="400px"
389 |       />
390 |     `);
391 | 
392 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
393 |     const pieSector = page.locator(".recharts-pie-sector").first();
394 |     await pieSector.hover();
395 | 
396 |     await expect(page.locator(tooltipContentSelector)).toBeVisible();
397 | 
398 |     // Text should have readable font size
399 |     const valueText = page.locator(tooltipValueSelector);
400 |     await expect(valueText).toHaveCSS("font-size", /\d+px/);
401 | 
402 |     // Should have proper contrast (background vs text)
403 |     const tooltipContainer = page.locator(tooltipContentSelector);
404 |     const backgroundColor = await tooltipContainer.evaluate(
405 |       (el) => window.getComputedStyle(el).backgroundColor,
406 |     );
407 |     expect(backgroundColor).not.toBe("transparent");
408 |   });
409 | 
410 |   test("tooltip appears on keyboard navigation", async ({ initTestBed, page }) => {
411 |     await initTestBed(`
412 |       <PieChart
413 |         nameKey="name"
414 |         dataKey="value"
415 |         data="{${sampleData}}"
416 |         width="400px"
417 |         height="400px"
418 |       />
419 |     `);
420 | 
421 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
422 | 
423 |     // Focus on chart and use keyboard navigation
424 |     await page.keyboard.press("Tab");
425 | 
426 |     // Chart should be focusable and tooltip may appear on focus
427 |     const focusedElement = page.locator(":focus");
428 |     await expect(focusedElement).toBeVisible();
429 |   });
430 | });
431 | 
432 | // =============================================================================
433 | // PERFORMANCE AND EDGE CASES
434 | // =============================================================================
435 | 
436 | test.describe("Performance and Edge Cases", () => {
437 |   test("handles rapid hover events efficiently", async ({ initTestBed, page }) => {
438 |     await initTestBed(`
439 |       <PieChart
440 |         nameKey="name"
441 |         dataKey="value"
442 |         data="{${sampleData}}"
443 |         width="400px"
444 |         height="400px"
445 |       />
446 |     `);
447 | 
448 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
449 | 
450 |     const sectors = page.locator(".recharts-pie-sector");
451 | 
452 |     // Rapidly hover over different sectors
453 |     for (let i = 0; i < 3; i++) {
454 |       await sectors.nth(i).hover();
455 |       await page.waitForTimeout(100);
456 |     }
457 | 
458 |     // Tooltip should still work correctly
459 |     await sectors.first().hover();
460 |     await expect(page.locator(tooltipContentSelector)).toBeVisible();
461 |   });
462 | 
463 |   test("handles empty or null data gracefully", async ({ initTestBed, page }) => {
464 |     const emptyData = `[]`;
465 | 
466 |     await initTestBed(`
467 |       <PieChart
468 |         nameKey="name"
469 |         dataKey="value"
470 |         data="{${emptyData}}"
471 |         width="400px"
472 |         height="400px"
473 |       />
474 |     `);
475 | 
476 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
477 | 
478 |     // Should not crash with empty data
479 |     // Tooltip won't appear since there's no data to hover over
480 |     const chart = page.locator(chartRoot);
481 |     await expect(chart).toBeVisible();
482 |   });
483 | 
484 |   test("handles special characters and long text", async ({ initTestBed, page }) => {
485 |     await initTestBed(`
486 |       <PieChart
487 |         nameKey="name"
488 |         dataKey="value"
489 |         data="{[
490 |           { name: 'Category with quotes & symbols', value: 400 },
491 |           { name: 'Very long category name that might wrap to multiple lines in the tooltip', value: 300 }
492 |         ]}"
493 |         width="400px"
494 |         height="400px"
495 |       />
496 |     `);
497 | 
498 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
499 |     const pieSector = page.locator(".recharts-pie-sector").first();
500 |     await pieSector.hover();
501 | 
502 |     await expect(page.locator(tooltipContentSelector)).toBeVisible();
503 | 
504 |     // Should handle special characters correctly
505 |     const nameText = page.locator(tooltipNameSelector);
506 |     await expect(nameText).toContainText("quotes");
507 |     await expect(page.locator(tooltipValueSelector)).toContainText("400");
508 |   });
509 | 
510 |   test("tooltip positioning adapts to chart boundaries", async ({ initTestBed, page }) => {
511 |     await initTestBed(`
512 |       <PieChart
513 |         nameKey="name"
514 |         dataKey="value"
515 |         data="{${sampleData}}"
516 |         width="200px"
517 |         height="200px"
518 |       />
519 |     `);
520 | 
521 |     await page.waitForSelector(chartRoot, { timeout: 10000 });
522 | 
523 |     // Hover near edge of small chart
524 |     const pieSector = page.locator(".recharts-pie-sector").first();
525 |     await pieSector.hover();
526 | 
527 |     await expect(page.locator(tooltipContentSelector)).toBeVisible();
528 | 
529 |     // Tooltip should be positioned within viewport
530 |     const tooltip = page.locator(tooltipSelector);
531 |     const boundingBox = await tooltip.boundingBox();
532 |     expect(boundingBox).toBeTruthy();
533 |     if (boundingBox) {
534 |       expect(boundingBox.x).toBeGreaterThanOrEqual(0);
535 |       expect(boundingBox.y).toBeGreaterThanOrEqual(0);
536 |     }
537 |   });
538 | });
539 | 
```

--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/process-switch-sync.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, expect, it, assert } from "vitest";
  2 | import { createEvalContext, parseStatements } from "./test-helpers";
  3 | import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync";
  4 | 
  5 | describe("Process switch statements (sync)", () => {
  6 |   it("no case", () => {
  7 |     // --- Arrange
  8 |     const source = `
  9 |             let x = 0;
 10 |             switch (x) {
 11 |             }
 12 |         `;
 13 |     const evalContext = createEvalContext({});
 14 |     const statements = parseStatements(source);
 15 | 
 16 |     // --- Act
 17 |     processStatementQueue(statements, evalContext);
 18 | 
 19 |     // --- Assert
 20 |     const thread = evalContext.mainThread!;
 21 |     expect(thread.blocks!.length).equal(1);
 22 |     expect(thread.blocks![0].vars.x).equal(0);
 23 |   });
 24 | 
 25 |   it("no matching case #1", () => {
 26 |     // --- Arrange
 27 |     const source = `
 28 |             let x = 0;
 29 |             let y = 0;
 30 |             switch (x) {
 31 |               case 3:
 32 |                 y++;
 33 |             }
 34 |         `;
 35 |     const evalContext = createEvalContext({});
 36 |     const statements = parseStatements(source);
 37 | 
 38 |     // --- Act
 39 |     processStatementQueue(statements, evalContext);
 40 | 
 41 |     // --- Assert
 42 |     const thread = evalContext.mainThread!;
 43 |     expect(thread.blocks!.length).equal(1);
 44 |     expect(thread.blocks![0].vars.y).equal(0);
 45 |   });
 46 | 
 47 |   it("no matching case #2", () => {
 48 |     // --- Arrange
 49 |     const source = `
 50 |             let x = 0;
 51 |             let y = 0;
 52 |             switch (x) {
 53 |               case 2:
 54 |                 y++;
 55 |                 break;
 56 |               case 3:
 57 |                 y++;
 58 |                 break;
 59 |             }
 60 |         `;
 61 |     const evalContext = createEvalContext({});
 62 |     const statements = parseStatements(source);
 63 | 
 64 |     // --- Act
 65 |     processStatementQueue(statements, evalContext);
 66 | 
 67 |     // --- Assert
 68 |     const thread = evalContext.mainThread!;
 69 |     expect(thread.blocks!.length).equal(1);
 70 |     expect(thread.blocks![0].vars.y).equal(0);
 71 |   });
 72 | 
 73 |   it("no matching case #3", () => {
 74 |     // --- Arrange
 75 |     const source = `
 76 |             let x = 0;
 77 |             let y = 0;
 78 |             switch (x) {
 79 |               case 1:
 80 |                 y++;
 81 |               case 2:
 82 |                 y++;
 83 |                 break;
 84 |               case 3:
 85 |                 y++;
 86 |                 break;
 87 |             }
 88 |         `;
 89 |     const evalContext = createEvalContext({});
 90 |     const statements = parseStatements(source);
 91 | 
 92 |     // --- Act
 93 |     processStatementQueue(statements, evalContext);
 94 | 
 95 |     // --- Assert
 96 |     const thread = evalContext.mainThread!;
 97 |     expect(thread.blocks!.length).equal(1);
 98 |     expect(thread.blocks![0].vars.y).equal(0);
 99 |   });
100 | 
101 |   it("matching case #1", () => {
102 |     // --- Arrange
103 |     const source = `
104 |             let x = 0;
105 |             let y = 0;
106 |             switch (x) {
107 |               case 0:
108 |                 y++;
109 |                 break;
110 |               case 1:
111 |                 y++;
112 |                 break;
113 |               case 2:
114 |                 y++;
115 |                 break;
116 |               case 3:
117 |                 y++;
118 |                 break;
119 |             }
120 |         `;
121 |     const evalContext = createEvalContext({});
122 |     const statements = parseStatements(source);
123 | 
124 |     // --- Act
125 |     processStatementQueue(statements, evalContext);
126 | 
127 |     // --- Assert
128 |     const thread = evalContext.mainThread!;
129 |     expect(thread.blocks!.length).equal(1);
130 |     expect(thread.blocks![0].vars.y).equal(1);
131 |   });
132 | 
133 |   it("matching case #2", () => {
134 |     // --- Arrange
135 |     const source = `
136 |             let x = 0;
137 |             let y = 0;
138 |             switch (x) {
139 |               default:
140 |                 y++;
141 |                 break;  
142 |               case 1:
143 |                 y++;
144 |                 break;
145 |               case 2:
146 |                 y++;
147 |                 break;
148 |               case 3:
149 |                 y++;
150 |                 break;
151 |             }
152 |         `;
153 |     const evalContext = createEvalContext({});
154 |     const statements = parseStatements(source);
155 | 
156 |     // --- Act
157 |     processStatementQueue(statements, evalContext);
158 | 
159 |     // --- Assert
160 |     const thread = evalContext.mainThread!;
161 |     expect(thread.blocks!.length).equal(1);
162 |     expect(thread.blocks![0].vars.y).equal(1);
163 |   });
164 | 
165 |   it("matching case #3", () => {
166 |     // --- Arrange
167 |     const source = `
168 |             let x = 0;
169 |             let y = 0;
170 |             switch (x) {
171 |               case 1:
172 |                 y++;
173 |                 break;
174 |               default:
175 |                 y++;
176 |                 break;  
177 |               case 2:
178 |                 y++;
179 |                 break;
180 |               case 3:
181 |                 y++;
182 |                 break;
183 |             }
184 |         `;
185 |     const evalContext = createEvalContext({});
186 |     const statements = parseStatements(source);
187 | 
188 |     // --- Act
189 |     processStatementQueue(statements, evalContext);
190 | 
191 |     // --- Assert
192 |     const thread = evalContext.mainThread!;
193 |     expect(thread.blocks!.length).equal(1);
194 |     expect(thread.blocks![0].vars.y).equal(1);
195 |   });
196 | 
197 |   it("matching case #4", () => {
198 |     // --- Arrange
199 |     const source = `
200 |             let x = 0;
201 |             let y = 0;
202 |             switch (x) {
203 |               case 1:
204 |                 y++;
205 |                 break;
206 |               case 2:
207 |                 y++;
208 |                 break;
209 |               default:
210 |                 y++;
211 |                 break;  
212 |               case 3:
213 |                 y++;
214 |                 break;
215 |             }
216 |         `;
217 |     const evalContext = createEvalContext({});
218 |     const statements = parseStatements(source);
219 | 
220 |     // --- Act
221 |     processStatementQueue(statements, evalContext);
222 | 
223 |     // --- Assert
224 |     const thread = evalContext.mainThread!;
225 |     expect(thread.blocks!.length).equal(1);
226 |     expect(thread.blocks![0].vars.y).equal(1);
227 |   });
228 | 
229 |   it("matching case #5", () => {
230 |     // --- Arrange
231 |     const source = `
232 |             let x = 0;
233 |             let y = 0;
234 |             switch (x) {
235 |               case 1:
236 |                 y++;
237 |                 break;
238 |               case 2:
239 |                 y++;
240 |                 break;
241 |               case 3:
242 |                 y++;
243 |                 break;
244 |               default:
245 |                 y++;
246 |                 break;  
247 |             }
248 |         `;
249 |     const evalContext = createEvalContext({});
250 |     const statements = parseStatements(source);
251 | 
252 |     // --- Act
253 |     processStatementQueue(statements, evalContext);
254 | 
255 |     // --- Assert
256 |     const thread = evalContext.mainThread!;
257 |     expect(thread.blocks!.length).equal(1);
258 |     expect(thread.blocks![0].vars.y).equal(1);
259 |   });
260 | 
261 |   it("matching case, fall-through #1", () => {
262 |     // --- Arrange
263 |     const source = `
264 |             let x = 0;
265 |             let y = 0;
266 |             switch (x) {
267 |               case 0:
268 |                 y++;
269 |               case 1:
270 |                 y++;
271 |                 break;
272 |               case 2:
273 |                 y++;
274 |                 break;
275 |               case 3:
276 |                 y++;
277 |                 break;
278 |             }
279 |         `;
280 |     const evalContext = createEvalContext({});
281 |     const statements = parseStatements(source);
282 | 
283 |     // --- Act
284 |     processStatementQueue(statements, evalContext);
285 | 
286 |     // --- Assert
287 |     const thread = evalContext.mainThread!;
288 |     expect(thread.blocks!.length).equal(1);
289 |     expect(thread.blocks![0].vars.y).equal(2);
290 |   });
291 | 
292 |   it("matching case, fall-through #2", () => {
293 |     // --- Arrange
294 |     const source = `
295 |             let x = 0;
296 |             let y = 0;
297 |             switch (x) {
298 |               case 0:
299 |                 y++;
300 |               case 1:
301 |                 y++;
302 |               case 2:
303 |                 y++;
304 |                 break;
305 |               case 3:
306 |                 y++;
307 |                 break;
308 |             }
309 |         `;
310 |     const evalContext = createEvalContext({});
311 |     const statements = parseStatements(source);
312 | 
313 |     // --- Act
314 |     processStatementQueue(statements, evalContext);
315 | 
316 |     // --- Assert
317 |     const thread = evalContext.mainThread!;
318 |     expect(thread.blocks!.length).equal(1);
319 |     expect(thread.blocks![0].vars.y).equal(3);
320 |   });
321 | 
322 |   it("matching case, fall-through #3", () => {
323 |     // --- Arrange
324 |     const source = `
325 |             let x = 0;
326 |             let y = 0;
327 |             switch (x) {
328 |               case 0:
329 |                 y++;
330 |               case 1:
331 |                 y++;
332 |               case 2:
333 |                 y++;
334 |               case 3:
335 |                 y++;
336 |                 break;
337 |             }
338 |         `;
339 |     const evalContext = createEvalContext({});
340 |     const statements = parseStatements(source);
341 | 
342 |     // --- Act
343 |     processStatementQueue(statements, evalContext);
344 | 
345 |     // --- Assert
346 |     const thread = evalContext.mainThread!;
347 |     expect(thread.blocks!.length).equal(1);
348 |     expect(thread.blocks![0].vars.y).equal(4);
349 |   });
350 | 
351 |   it("matching case, fall-through #4", () => {
352 |     // --- Arrange
353 |     const source = `
354 |             let x = 0;
355 |             let y = 0;
356 |             switch (x) {
357 |               case 0:
358 |                 y++;
359 |               case 1:
360 |                 y++;
361 |               case 2:
362 |                 y++;
363 |               case 3:
364 |                 y++;
365 |             }
366 |         `;
367 |     const evalContext = createEvalContext({});
368 |     const statements = parseStatements(source);
369 | 
370 |     // --- Act
371 |     processStatementQueue(statements, evalContext);
372 | 
373 |     // --- Assert
374 |     const thread = evalContext.mainThread!;
375 |     expect(thread.blocks!.length).equal(1);
376 |     expect(thread.blocks![0].vars.y).equal(4);
377 |   });
378 | 
379 |   it("switch in loop #1", () => {
380 |     // --- Arrange
381 |     const source = `
382 |             let x = 0;
383 |             let y = 0;
384 |             while (x < 4) {
385 |               switch (x) {
386 |                 case 0:
387 |                   y++;
388 |                 case 1:
389 |                   y++;
390 |                 case 2:
391 |                   y++;
392 |                 case 3:
393 |                   y++;
394 |               }
395 |               x++;
396 |             }  
397 |         `;
398 |     const evalContext = createEvalContext({});
399 |     const statements = parseStatements(source);
400 | 
401 |     // --- Act
402 |     processStatementQueue(statements, evalContext);
403 | 
404 |     // --- Assert
405 |     const thread = evalContext.mainThread!;
406 |     expect(thread.blocks!.length).equal(1);
407 |     expect(thread.blocks![0].vars.y).equal(10);
408 |   });
409 | 
410 |   it("switch in loop #2", () => {
411 |     // --- Arrange
412 |     const source = `
413 |             let x = 0;
414 |             let y = 0;
415 |             while (x < 4) {
416 |               switch (x) {
417 |                 case 0:
418 |                   y++;
419 |                 case 1:
420 |                   y++;
421 |                 case 2:
422 |                   y++;
423 |                 case 3:
424 |                   y++;
425 |               }
426 |               x++;
427 |               break;
428 |             }  
429 |         `;
430 |     const evalContext = createEvalContext({});
431 |     const statements = parseStatements(source);
432 | 
433 |     // --- Act
434 |     processStatementQueue(statements, evalContext);
435 | 
436 |     // --- Assert
437 |     const thread = evalContext.mainThread!;
438 |     expect(thread.blocks!.length).equal(1);
439 |     expect(thread.blocks![0].vars.y).equal(4);
440 |   });
441 | 
442 |   it("switch in loop #3", () => {
443 |     // --- Arrange
444 |     const source = `
445 |             let x = 0;
446 |             let y = 0;
447 |             while (x < 4) {
448 |               switch (x) {
449 |                 case 0:
450 |                   y++;
451 |                   break;
452 |                 case 1:
453 |                   y++;
454 |                   break;
455 |                 case 2:
456 |                   y++;
457 |                   break;
458 |                 case 3:
459 |                   y++;
460 |                   break;
461 |               }
462 |               x++;
463 |             }  
464 |         `;
465 |     const evalContext = createEvalContext({});
466 |     const statements = parseStatements(source);
467 | 
468 |     // --- Act
469 |     processStatementQueue(statements, evalContext);
470 | 
471 |     // --- Assert
472 |     const thread = evalContext.mainThread!;
473 |     expect(thread.blocks!.length).equal(1);
474 |     expect(thread.blocks![0].vars.y).equal(4);
475 |   });
476 | 
477 |   it("switch in loop #4", () => {
478 |     // --- Arrange
479 |     const source = `
480 |             let x = 0;
481 |             let y = 0;
482 |             while (x < 4) {
483 |               switch (x) {
484 |                 case 0:
485 |                   y++;
486 |                   break;
487 |                 case 1:
488 |                   y++;
489 |                   break;
490 |                 case 2:
491 |                   y++;
492 |                 case 3:
493 |                   y++;
494 |               }
495 |               x++;
496 |             }  
497 |         `;
498 |     const evalContext = createEvalContext({});
499 |     const statements = parseStatements(source);
500 | 
501 |     // --- Act
502 |     processStatementQueue(statements, evalContext);
503 | 
504 |     // --- Assert
505 |     const thread = evalContext.mainThread!;
506 |     expect(thread.blocks!.length).equal(1);
507 |     expect(thread.blocks![0].vars.y).equal(5);
508 |   });
509 | 
510 |   it("switch in loop #5 (continue)", () => {
511 |     // --- Arrange
512 |     const source = `
513 |             let x = 0;
514 |             let y = 0;
515 |             while (x < 4) {
516 |               switch (x) {
517 |                 case 0:
518 |                   y++;
519 |                   break;
520 |                 case 1:
521 |                   y++;
522 |                   break;
523 |                 case 2:
524 |                   y++;
525 |                   x += 3;
526 |                   continue;
527 |                 case 3:
528 |                   y++;
529 |               }
530 |               x++;
531 |             }
532 |         `;
533 |     const evalContext = createEvalContext({});
534 |     const statements = parseStatements(source);
535 | 
536 |     // --- Act
537 |     processStatementQueue(statements, evalContext);
538 | 
539 |     // --- Assert
540 |     const thread = evalContext.mainThread!;
541 |     expect(thread.blocks!.length).equal(1);
542 |     expect(thread.blocks![0].vars.y).equal(3);
543 |   });
544 | 
545 |   it("switch in loop #6 (return)", () => {
546 |     // --- Arrange
547 |     const source = `
548 |             let x = 0;
549 |             let y = 0;
550 |             while (x < 4) {
551 |               switch (x) {
552 |                 case 0:
553 |                   y++;
554 |                   break;
555 |                 case 1:
556 |                   y++;
557 |                   break;
558 |                 case 2:
559 |                   y++;
560 |                   x += 3;
561 |                   return;
562 |                 case 3:
563 |                   y++;
564 |               }
565 |               x++;
566 |             }
567 |         `;
568 |     const evalContext = createEvalContext({});
569 |     const statements = parseStatements(source);
570 | 
571 |     // --- Act
572 |     processStatementQueue(statements, evalContext);
573 | 
574 |     // --- Assert
575 |     const thread = evalContext.mainThread!;
576 |     expect(thread.blocks![0].vars.y).equal(3);
577 |   });
578 | 
579 |   it("switch in loop #6 (return)", () => {
580 |     // --- Arrange
581 |     const source = `
582 |             let x = 0;
583 |             let y = 0;
584 |             while (x < 4) {
585 |               switch (x) {
586 |                 case 0:
587 |                   y++;
588 |                   break;
589 |                 case 1:
590 |                   y++;
591 |                   break;
592 |                 case 2:
593 |                   y++;
594 |                   x += 3;
595 |                   return;
596 |                 case 3:
597 |                   y++;
598 |               }
599 |               x++;
600 |             }
601 |         `;
602 |     const evalContext = createEvalContext({});
603 |     const statements = parseStatements(source);
604 | 
605 |     // --- Act
606 |     processStatementQueue(statements, evalContext);
607 | 
608 |     // --- Assert
609 |     const thread = evalContext.mainThread!;
610 |     expect(thread.blocks![0].vars.y).equal(3);
611 |   });
612 | 
613 |   it("switch has its own scope", () => {
614 |     // --- Arrange
615 |     const source = `
616 |             let x = 0;
617 |             let y = 0;
618 |             let z = 0;
619 |             switch (x) {
620 |               case 0:
621 |                 let y = 3;
622 |                 z = y + 1;
623 |                 break;
624 |               case 1:
625 |                 y++;
626 |                 break;
627 |             }
628 |         `;
629 |     const evalContext = createEvalContext({});
630 |     const statements = parseStatements(source);
631 | 
632 |     // --- Act
633 |     processStatementQueue(statements, evalContext);
634 | 
635 |     // --- Assert
636 |     const thread = evalContext.mainThread!;
637 |     expect(thread.blocks![0].vars.y).equal(0);
638 |     expect(thread.blocks![0].vars.z).equal(4);
639 |   });
640 | 
641 |   it("switch fails with multiple let", () => {
642 |     // --- Arrange
643 |     const source = `
644 |             let x = 0;
645 |             let y = 0;
646 |             switch (x) {
647 |               case 0:
648 |                 let z = 3;
649 |               case 1:
650 |                 let z = 4;
651 |                 y++;
652 |                 break;
653 |             }
654 |         `;
655 |     const evalContext = createEvalContext({});
656 |     const statements = parseStatements(source);
657 | 
658 |     // --- Act/Assert
659 |     try {
660 |       processStatementQueue(statements, evalContext);
661 |     } catch (err: any) {
662 |       expect(err.toString().includes("already declared")).equal(true);
663 |       return;
664 |     }
665 |     assert.fail("Exception expected");
666 |   });
667 | 
668 |   it("switch fails with multiple const/let", () => {
669 |     // --- Arrange
670 |     const source = `
671 |             let x = 0;
672 |             let y = 0;
673 |             switch (x) {
674 |               case 0:
675 |                 let z = 3;
676 |               case 1:
677 |                 const z = 4;
678 |                 y++;
679 |                 break;
680 |             }
681 |         `;
682 |     const evalContext = createEvalContext({});
683 |     const statements = parseStatements(source);
684 | 
685 |     // --- Act/Assert
686 |     try {
687 |       processStatementQueue(statements, evalContext);
688 |     } catch (err: any) {
689 |       expect(err.toString().includes("already declared")).equal(true);
690 |       return;
691 |     }
692 |     assert.fail("Exception expected");
693 |   });
694 | 
695 |   it("switch fails with multiple const", () => {
696 |     // --- Arrange
697 |     const source = `
698 |             let x = 0;
699 |             let y = 0;
700 |             switch (x) {
701 |               case 0:
702 |                 const z = 3;
703 |               case 1:
704 |                 const z = 4;
705 |                 y++;
706 |                 break;
707 |             }
708 |         `;
709 |     const evalContext = createEvalContext({});
710 |     const statements = parseStatements(source);
711 | 
712 |     // --- Act/Assert
713 |     try {
714 |       processStatementQueue(statements, evalContext);
715 |     } catch (err: any) {
716 |       expect(err.toString().includes("already declared")).equal(true);
717 |       return;
718 |     }
719 |     assert.fail("Exception expected");
720 |   });
721 | });
722 | 
```

--------------------------------------------------------------------------------
/docs/public/pages/globals.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Global Functions and Variables
  2 | 
  3 | XMLUI offers dozens of global functions and properties for use in XMLScript code. This document provides a detailed description of them.
  4 | 
  5 | ## Actions Namespace
  6 | 
  7 | XMLUI provides an `Actions` namespace to collect frequently used application actions you can invoke from scripts. When calling them, use the `Actions` namespace like in the following example:
  8 | 
  9 | ```ts
 10 | Actions.callApi({ url: "/api/contacts/123", method: "delete" });
 11 | ```
 12 | 
 13 | ### `Actions.callApi`
 14 | 
 15 | ```ts
 16 | function Actions.callApi(options: { 
 17 |   method: "get" | "post" | "put" | "delete";
 18 |   url: string;
 19 |   rawBody?: any;
 20 |   body?: any;
 21 |   queryParams?: Record<string, any>;
 22 |   headers?: Record<string, any>;
 23 |   payloadType?: string;
 24 |   invalidates?: string | string[];
 25 |   updates?: string | string[];
 26 |   confirmTitle?: string;
 27 |   confirmMessage?: string;
 28 |   confirmButtonLabel?: string;
 29 |   beforeRequest?: string;
 30 |   onSuccess?: string;
 31 |   onProgress?: string;
 32 |   onError?: string;
 33 |   params: any;
 34 |   optimisticValue: any;
 35 |   when: string;
 36 |   getOptimisticValue: string;
 37 |   inProgressNotificationMessage?: string;
 38 |   completedNotificationMessage?: string;
 39 |   errorNotificationMessage?: string;
 40 |   uid?: string;
 41 | }): Promise<any>;
 42 | ```
 43 | 
 44 | This function invokes an API endpoint with the specified options.
 45 | 
 46 | ### `Actions.download`
 47 | 
 48 | ```ts
 49 | function Actions.download(options: { 
 50 |   fileName: string;
 51 |   params: any;
 52 |   method: "get" | "post" | "put" | "delete";
 53 |   url: string;
 54 |   rawBody?: any;
 55 |   body?: any;
 56 |   queryParams?: Record<string, any>;
 57 |   headers?: Record<string, any>;
 58 |   payloadType?: string;
 59 | }): Promise<any>;
 60 | ```
 61 | 
 62 | This function downloads a file from the specified URL.
 63 | 
 64 | ### `Actions.upload`
 65 | 
 66 | ```ts 
 67 | function Actions.upload(options: { 
 68 |   file: File;
 69 |   formParams?: Record<string, any>;
 70 |   asForm?: boolean;
 71 |   method: "get" | "post" | "put" | "delete";
 72 |   url: string;
 73 |   rawBody?: any;
 74 |   body?: any;
 75 |   queryParams?: Record<string, any>;
 76 |   headers?: Record<string, any>;
 77 |   payloadType?: string;
 78 |   invalidates?: string | string[];
 79 |   params: any;
 80 |   chunkSizeInBytes?: number;
 81 |   onError?: string;
 82 |   onProgress?: (...args: any) => void;
 83 | }): Promise<any>;
 84 | ```
 85 | 
 86 | This function uploads a file to the specified URL.
 87 | 
 88 | ## App-Specific Globals
 89 | 
 90 | ### `appGlobals`
 91 | 
 92 | ```ts
 93 | get appGlobals: Record<string, any>;
 94 | ```
 95 | 
 96 | This property returns a hash object containing all application-global settings defined in the app's configuration file.
 97 | 
 98 | ### `environment`
 99 | 
100 | ```ts
101 | get environment: {
102 |   isWindowFocused: boolean;
103 | };
104 | ```
105 | 
106 | Returns an object with some properties of the current environment.
107 | 
108 | ### `mediaSize`
109 | 
110 | ```ts
111 | get mediaSize: {
112 |   phone: boolean;
113 |   landscapePhone: boolean;
114 |   tablet: boolean;
115 |   desktop: boolean;
116 |   largeDesktop: boolean;
117 |   xlDesktop: boolean;
118 |   smallScreen: boolean;
119 |   largeScreen: boolean;
120 |   size: "xs" | "sm" | "md" | "lg" | "xl" | "xxl";
121 | };
122 | ```
123 | 
124 | This property returns an object with information about the current media size. The `phone`, `landscapePhone`, `tablet`, `desktop`, `largeDesktop`, and `xlDesktop` flogs indicate the current app window's viewport size. Only one of these flags is `true`; the others are `false`.
125 | `smallScreen` is set to `true` if the current size is less than `desktop`. `largeScreen` is set to `true` if the current size is `desktop` or bigger.
126 | 
127 | ### `standalone`
128 | 
129 | ```ts
130 | get standalone: boolean;
131 | ```
132 | 
133 | This property returns `true` if the app is a standalone XMLUI app; otherwise (for example, as part of a website), it returns `false`.
134 | 
135 | ## Date Utilities
136 | 
137 | ### `differenceInMinutes`
138 | 
139 | ```ts
140 | function differenceInMinutes: (date1: number | Date, date2: number | Date): number;
141 | ```
142 | 
143 | This function returns the difference between the two date parameters in minutes.
144 | 
145 | ### `formatDate`
146 | 
147 | ```ts
148 | function formatDate(date: string | Date, formatString?: string): string;
149 | ```
150 | 
151 | This function formats the specified value's date part. When called without a `formatString` parameter, it returns a local date string according to the machine's local settings. When a `formatString` is provided, it uses [date-fns format patterns](https://date-fns.org/docs/format) to format the date.
152 | 
153 | **Examples:**
154 | 
155 | ```ts
156 | formatDate(new Date())                    // "10/20/2025" (localized)
157 | formatDate(new Date(), "MMM dd, yyyy")    // "Oct 20, 2025"
158 | formatDate(new Date(), "yyyy-MM-dd")      // "2025-10-20"
159 | formatDate(new Date(), "'Today is' EEEE") // "Today is Monday"
160 | ```
161 | 
162 | **Common format patterns:**
163 | - `yyyy` - Full year (2025)
164 | - `yy` - Two-digit year (25)
165 | - `MMMM` - Full month name (October)
166 | - `MMM` - Abbreviated month name (Oct)
167 | - `MM` - Month as number with leading zero (10)
168 | - `M` - Month as number (10)
169 | - `dd` - Day of month with leading zero (20)
170 | - `d` - Day of month (20)
171 | - `EEEE` - Full day of week (Monday)
172 | - `EEE` - Abbreviated day of week (Mon)
173 | - `HH` - Hour in 24-hour format with leading zero (14)
174 | - `H` - Hour in 24-hour format (14)
175 | - `hh` - Hour in 12-hour format with leading zero (02)
176 | - `h` - Hour in 12-hour format (2)
177 | - `mm` - Minutes with leading zero (05)
178 | - `m` - Minutes (5)
179 | - `ss` - Seconds with leading zero (09)
180 | - `s` - Seconds (9)
181 | - `a` - AM/PM marker (AM, PM)
182 | - Use single quotes `'` to escape literal text
183 | 
184 | ### `formatDateTime`
185 | 
186 | ```ts
187 | function formatDateTime(date: string | Date, formatString?: string): string;
188 | ```
189 | 
190 | This function formats the specified value into a date and time string. When called without a `formatString` parameter, it returns a local date and time string according to the machine's local settings. When a `formatString` is provided, it uses [date-fns format patterns](https://date-fns.org/docs/format).
191 | 
192 | **Examples:**
193 | 
194 | ```ts
195 | formatDateTime(new Date())                           // "10/20/2025, 2:30:45 PM" (localized)
196 | formatDateTime(new Date(), "MMM dd, yyyy HH:mm:ss")  // "Oct 20, 2025 14:30:45"
197 | formatDateTime(new Date(), "yyyy-MM-dd'T'HH:mm:ss")  // "2025-10-20T14:30:45"
198 | formatDateTime(new Date(), "EEEE, MMMM d 'at' h:mm a") // "Monday, October 20 at 2:30 PM"
199 | ```
200 | 
201 | See [common format patterns](#formatdate) in the `formatDate` documentation.
202 | 
203 | ### `formatDateWithoutYear`
204 | 
205 | ```ts
206 | function formatDateWithoutYear(date: string | Date): string;
207 | ```
208 | 
209 | This function formats the specified value's date part (without year) into a local date string (according to the machine's local settings).
210 | 
211 | ### `formatHumanElapsedTime`
212 | 
213 | ```ts
214 | function formatHumanElapsedTime(date: string | Date): string;
215 | ```
216 | 
217 | This function formats a date into a human-readable elapsed time string. It returns strings like "now", "12 seconds ago", "3 minutes ago", "2 hours ago", "yesterday", "3 days ago", "2 weeks ago", "6 months ago", "1 year ago" based on how much time has passed since the given date.
218 | 
219 | ### `formatTime`
220 | 
221 | ```ts
222 | function formatTime(date: string | Date, formatString?: string): string;
223 | ```
224 | 
225 | This function formats the specified value's time part. When called without a `formatString` parameter, it returns a local time string according to the machine's local settings. When a `formatString` is provided, it uses [date-fns format patterns](https://date-fns.org/docs/format).
226 | 
227 | **Examples:**
228 | 
229 | ```ts
230 | formatTime(new Date())                    // "2:30:45 PM" (localized)
231 | formatTime(new Date(), "HH:mm:ss")        // "14:30:45"
232 | formatTime(new Date(), "h:mm a")          // "2:30 PM"
233 | formatTime(new Date(), "HH:mm")           // "14:30"
234 | formatTime(new Date(), "hh:mm:ss a")      // "02:30:45 PM"
235 | ```
236 | 
237 | See [common format patterns](#formatdate) in the `formatDate` documentation.
238 | 
239 | ### `formatTimeWithoutSeconds`
240 | 
241 | ```ts
242 | function formatTimeWithoutSeconds(date: string | Date): string;
243 | ```
244 | 
245 | Format the specified value's time part (without seconds) into a local date string (according to the machine's local settings).
246 | 
247 | ### `getDate`
248 | 
249 | ```ts
250 | function function getDate(date?: string | number | Date): Date;
251 | ```
252 | 
253 | This function creates a date from the specified input value. If no input is provided, it returns the current date and time.
254 | 
255 | ### `getDateUntilNow`
256 | 
257 | ```ts
258 | function function getDateUntilNow(date?: string | number | Date): string;
259 | ```
260 | 
261 | This function calculates the difference between the current date and the provided one and returns it in a human-readable form, such as "1 month", "2 weeks", etc.
262 | 
263 | ### `isoDateString`
264 | 
265 | ```ts
266 | function isoDateString(date?: string): string;
267 | ```
268 | 
269 | This function converts the input string into a date value and returns the ISO 8601 string representation of the date. It can pass dates between the UI and backend APIs in a standard format.
270 | 
271 | ### `differenceInMinutes`
272 | 
273 | ```ts
274 | function differenceInMinutes: (date1: number | Date, date2: number | Date): number;
275 | ```
276 | 
277 | This function returns the difference between the two date parameters in minutes.
278 | 
279 | ### `isSameDay`
280 | 
281 | ```ts
282 | function isSameDay: (dateLeft: number | Date, dateRight: number | Date): boolean;
283 | ```
284 | 
285 | This function checks if the two dates specified in the parameters are on the same calendar day.
286 | 
287 | ### `isThisYear`
288 | 
289 | ```ts
290 | function (date: Date | number): boolean;
291 | ```
292 | 
293 | This function checks if the specified date is in this calendar year.
294 | 
295 | ### `isTomorrow`
296 | 
297 | ```ts
298 | function isTomorrow(date?: string | Date): boolean;
299 | ```
300 | 
301 | This function checks if the specified date is tomorrow.
302 | 
303 | ### `isYesterday`
304 | 
305 | ```ts
306 | function isYesterday(date?: string | number | Date): boolean;
307 | ```
308 | 
309 | This function checks if the specified date is yesterday.
310 | 
311 | ### `smartFormatDate`
312 | 
313 | ```ts
314 | function function smartFormatDate(date?: string | number | Date): string;
315 | ```
316 | 
317 | This function checks the date value provided for some particular values and returns accordingly. Otherwise, returns it as `formatDate` would. Special values returned:
318 | - No date value provided: "-"
319 | - The date is today: returns only the date value with `formatDate`
320 | - The date is this week: return the relative time difference, such as "last Monday"
321 | 
322 | ### `smartFormatDateTime`
323 | 
324 | ```ts
325 | function function smartFormatDateTime(date?: string | number | Date): string;
326 | ```
327 | 
328 | This function checks the date value provided for some particular values and returns accordingly. Otherwise, returns it as `formatDateTime` would. Special values returned:
329 | - No date value provided: "-"
330 | - The date is today: returns only the time value with `formatTime`
331 | - The date is this week: return the relative time difference, such as "last Monday at 2:00 AM"
332 | 
333 | ## Engine-Related Globals
334 | 
335 | ### `version`
336 | 
337 | ```ts
338 | get version: string;
339 | ```
340 | 
341 | This property retrieves the version of the XMLUI engine.
342 | 
343 | 
344 | ## File Utilities
345 | 
346 | ### `formatFileSizeInBytes`
347 | 
348 | ```ts
349 | function formatFileSizeInBytes(size?: number): string;
350 | ```
351 | 
352 | This function returns the specified file size in a compact form, such as "112 B", "2.0 KiB", "23.4 KiB", "2.3 MiB", etc.
353 | 
354 | ### `getFileExtension`
355 | 
356 | ```ts
357 | function getFileExtension(filename: string, mimetype?: string): string;
358 | ```
359 | 
360 | This function returns the type of the specified file.  
361 | - If only `filename` is specified, return the extension inferred from the file name. 
362 | - If only `mime-type` is specified, it returns the type inferred from the type name.
363 | - If both are specified, and the inferred file extension equals the inferred mime type, that value is returned.
364 | - Otherwise, it returns undefined.
365 | 
366 | ## Math Utilities
367 | 
368 | ### `avg`
369 | 
370 | ```ts 
371 | function avg(numbers: number[], decimals?: number): number;
372 | ```
373 | 
374 | This function calculates the average of the specified numbers and rounds it to the specified number of `decimals`. If `decimals` is not specified, the result is not rounded.
375 | 
376 | ### `sum`
377 | 
378 | ```ts
379 | function sum(numbers: number[]): number;
380 | ```
381 | 
382 | This function calculates the sum of the specified numbers.
383 | 
384 | ## Navigation Utilities
385 | 
386 | ### `navigate`
387 | 
388 | ```ts
389 | function navigate(url: string): void;
390 | ```
391 | 
392 | This function navigates to the specified `url`.
393 | 
394 | ### `routerBaseName`
395 | 
396 | ```ts
397 | get routerBaseName: string;
398 | ```
399 | 
400 | This property gets the base name used for the router. 
401 | 
402 | ## Notifications and Dialogs
403 | 
404 | ### `confirm`
405 | 
406 | ```ts
407 | function confirm: (title: string, message?: string, actionLabel?: string): boolean
408 | ```
409 | 
410 | Instructs the browser to display a dialog with a confirmation message, and to wait until the user either confirms or cancels the dialog. It returns a boolean indicating whether OK (`true`) or Cancel (`false`) was selected.
411 | 
412 | - `title`: The title of the dialog
413 | - `message`: The message to display
414 | - `actionLabel`: The label of the action button
415 | 
416 | ### `signError`
417 | 
418 | ```ts
419 | function signError(error: Error | string): void;
420 | ```
421 | 
422 | This method displays the specified `error` (error message) on the UI.
423 | 
424 | ### `toast`
425 | 
426 | ```ts
427 | function toast(message: string, options: ToastOptions): void;
428 | ```
429 | 
430 | This function displays a neutral toast with the specified `message`.
431 | 
432 | All toast methods allow the using an option object (`ToastOptions`) with these properties:
433 | - `id` (string): an optional ID. This identifier can be used to remove the toast programmatically.
434 | - `duration` (number): The timeout of the notification (in milliseconds). After this timeout, the notification is removed from the screen.
435 | - `position` (string): The notification's position; one of these values: `top-left`, `top-center`, `top-right`,  `bottom-left`, `bottom-center`, or `bottom-right`.
436 | 
437 | Examples:
438 | ```js
439 | // --- Message for 2 seconds
440 | toast("Hello, world!", { duration: 2000 });
441 | 
442 | // --- Message at the top-left corner
443 | toast("Hello, world!", { position: "top-left" });
444 | ```
445 | 
446 | ### `toast.error`
447 | 
448 | ```ts
449 | function toast.error(message: string, options: ToastOptions): void;
450 | ```
451 | 
452 | This function displays a toast with the specified error notification `message`.
453 | 
454 | ### `toast.loading`
455 | 
456 | ```ts
457 | function toast.loading(message: string, options: ToastOptions): void;
458 | ```
459 | 
460 | This function displays a toast with the specified notification `message` indicating an operation's in-progress state.
461 | 
462 | ### `toast.remove`
463 | 
464 | ```ts
465 | function toast.remove(id: string): void;
466 | ```
467 | 
468 | This function removes the toast with the specified `id`.
469 | 
470 | ### `toast.success`
471 | 
472 | ```ts
473 | function toast.success(message: string, options: ToastOptions): void;
474 | ```
475 | 
476 | This function displays a toast with the specified success notification `message`.
477 | 
478 | ## Theme-Related
479 | 
480 | ### `activeThemeId`
481 | 
482 | ```ts
483 | get activeThemeId: string;
484 | ```
485 | 
486 | This property returns the ID of the currently active theme.
487 | 
488 | ### `activeThemeTone`
489 | 
490 | ```ts
491 | get activeThemeTone: string;
492 | ```
493 | 
494 | This property returns the tone of the currently active theme ("light" or "dark").
495 | 
496 | ### `availableThemeIds`
497 | 
498 | ```ts
499 | get availableThemeIds: string[];
500 | ```
501 | 
502 | This property returns an array of all available theme IDs.
503 | 
504 | ### `setTheme`
505 | 
506 | ```ts
507 | setTheme: (themId: string): void;
508 | ```
509 | 
510 | This function sets the current theme to the one with the specified `themeId`.
511 | 
512 | ### `setThemeTone`
513 | 
514 | ```ts
515 | setThemeTone: (tone: string): void;
516 | ```
517 | 
518 | This function sets the current theme tone to the specified `tone` value ("light" or "dark").
519 | 
520 | ### `toggleThemeTone`
521 | 
522 | ```ts
523 | toggleThemeTone: (): void;
524 | ```
525 | 
526 | This function toggles the current theme tone from "light" to "dark" or vice versa.
527 | 
528 | ## Users
529 | 
530 | XMLUI does not constrain what user information may contain; it may be a single identifier or a set of user-specific properties. The app is responsible for processing and displaying this information.
531 | 
532 | ### `loggedInUser`
533 | 
534 | ```ts
535 | get loggedInUser: any | null;
536 | ```
537 | 
538 | This property gets the information about the logged-in user. If `null`, no user is logged in.
539 | 
540 | 
541 | ### `setLoggedInUser`
542 | 
543 | ```ts
544 | setLoggedInUser: (loggedInUser: any): void;
545 | ```
546 | 
547 | This function sets the information about the logged-in user.
548 | 
549 | ## Various Functions
550 | 
551 | ### `capitalize`
552 | 
553 | ```ts
554 | function capitalize(input?: string): string;
555 | ```
556 | 
557 | Converts the first character of string to upper case and the remaining to lower case.
558 | 
559 | ### `defaultTo`
560 | 
561 | ```ts
562 | function defaultTo(value: any, defaultValue: any): any;
563 | ```
564 | 
565 | Provides a fallback value (`defaultValue`) if the specified `value` is null or undefined.
566 | 
567 | ### `delay`
568 | 
569 | ```ts
570 | function delay(ms: number): Promise<void>;
571 | ```
572 | 
573 | Delays the execution of the next line of code by the specified number of milliseconds.
574 | 
575 | ### `debounce`
576 | 
577 | ```ts
578 | function debounce<F extends (...args: any[]) => any>(
579 |   delayMs: number,
580 |   func: F,
581 |   ...args: any[]
582 | ): void;
583 | ```
584 | 
585 | Delays function execution until the specified time has elapsed since the last invocation. Useful for limiting expensive operations like API calls during user input.
586 | 
587 | ```xmlui copy
588 | <App>
589 |   <!-- Basic usage: pass value as argument -->
590 |   <TextBox 
591 |     label="Search:" 
592 |     onDidChange="e => debounce(500, (val) => console.log('Search:', val), e)"
593 |   />
594 | 
595 |   <!-- With API call -->
596 |   <TextBox 
597 |     label="Email:" 
598 |     onDidChange="e => debounce(1000, (email) => {
599 |       Actions.callApi({
600 |         url: '/api/validate-email',
601 |         body: { email }
602 |       });
603 |     }, e)"
604 |   />
605 | </App>
606 | ```
607 | 
608 | ### `distinct`
609 | 
610 | ```ts
611 | function distinct(arr: any[]): any[];
612 | ```
613 | 
614 | Returns an array with all duplicate values removed.
615 | 
616 | ### `getPropertyByPath`
617 | 
618 | ```ts
619 | function getPropertyByPath(obj: any, path: string): any;
620 | ```
621 | 
622 | Gets the property of `obj` at the specified `path`. `path` is a string with the chained property names separated by dots.
623 | 
624 | ### `pluralize`
625 | 
626 | ```ts
627 | function pluralize(value: number, singular: string, plural: string): string;
628 | ```
629 | 
630 | When the value is equal to one, returns `singular`; otherwise, `plural.`
631 | 
632 | ### `toHashObject`
633 | 
634 | ```ts
635 | function toHashObject(arr: any[], keyProp: string, valueProp: string): any;
636 | ```
637 | 
638 | This function converts an array of objects into a hash object.
639 | - `keyProp`: The name of the object property that acts as the key
640 | - `valueProp`: The name of the object property that acts as the value
641 | 
642 | Let's assume the following array of objects:
643 | ```json
644 | [
645 |   { "key": "key1", "value": "value1" },
646 |   { "key": "key2", "value": "value2" },
647 |   { "key": "key3", "value": "value3" }
648 | ]
649 | 
650 | Invoking `toHashObject` with the following parameters:
651 | 
652 | ```ts
653 | toHashObject(arr, "key", "value");
654 | ```
655 | 
656 | Returns the following object:
657 | 
658 | ```json
659 | {
660 |   "key1": "value1",
661 |   "key2": "value2",
662 |   "key3": "value3"
663 | }
664 | ```
665 | 
```

--------------------------------------------------------------------------------
/xmlui/tests-e2e/binding-regression.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { SKIP_REASON } from "../src/testing/component-test-helpers";
  2 | import { expect, test } from "../src/testing/fixtures";
  3 | import { initApp } from "../src/testing/themed-app-test-helpers";
  4 | 
  5 | test("Increment counter through arrow function works", async ({ page, initTestBed }) => {
  6 |   await initTestBed(`
  7 |     <Stack var.counter="{0}" var.incrementFunc="{(x) => { return ++x; }}">
  8 |       <Button
  9 |         testId="button"
 10 |         label="Increment counter: {counter}"
 11 |         onClick="counter = incrementFunc(counter)"
 12 |       />
 13 |     </Stack>  
 14 |   `);
 15 |   await page.getByTestId("button").click();
 16 |   await expect(page.getByTestId("button")).toHaveText("Increment counter: 1");
 17 | });
 18 | 
 19 | test("Array.filter works with the binding engine #1", async ({ page, initTestBed }) => {
 20 |   await initTestBed(`
 21 |     <Button
 22 |       testId="button" var.item="{ [5,4,3,2,1].filter(item => item % 2 === 0)[1] }"
 23 |       label="stuff: {item}" />
 24 |   `);
 25 | 
 26 |   await expect(page.getByTestId("button")).toHaveText("stuff: 2");
 27 | });
 28 | 
 29 | test("Array.filter works with the binding engine #2", async ({ page, initTestBed }) => {
 30 |   await initTestBed(`
 31 |     <Button
 32 |       testId="button" var.item="{ [5,4,3,2,1].filter(item => item % 2 === 0)[1] }"
 33 |       label="stuff: {item}" />`);
 34 |   await expect(page.getByTestId("button")).toHaveText("stuff: 2");
 35 | });
 36 | 
 37 | test("Array.reduce works with the binding engine", async ({ page, initTestBed }) => {
 38 |   await initTestBed(`
 39 |     <Button
 40 |       testId="button" var.array="{ [5,4,3,2,1] }"
 41 |       var.item="{ array.reduce((acc, item) => acc + item, 0) }"
 42 |       label="stuff: {item}" />`);
 43 |   await expect(page.getByTestId("button")).toHaveText("stuff: 15");
 44 | });
 45 | 
 46 | test("Action.delay works with the binding engine", async ({ page, initTestBed }) => {
 47 |   await initTestBed(
 48 |     `<Button
 49 |       testId="button"
 50 |       var.actionCounter="{0}"
 51 |       var.asyncTestActionInVars="{ (stuff) => { Actions.delay(100); return 123; } }"
 52 |       label="async process {actionCounter}"
 53 |       onClick="actionCounter = asyncTestActionInVars(5);"
 54 |     />`,
 55 |   );
 56 |   await page.getByTestId("button").click();
 57 |   await delay(200);
 58 |   await expect(page.getByTestId("button")).toHaveText("async process 123");
 59 | });
 60 | 
 61 | test("Button click with function in vars works", async ({ page, initTestBed }) => {
 62 |   await initTestBed(`
 63 |     <Button
 64 |       testId="button"
 65 |       var.label=""
 66 |       var.objectArgument="{{ list: [1, 2, 3] }}"
 67 |       var.testFn="{(arg) => { label = arg }}"
 68 |       label="{label}"
 69 |       onClick="testFn(objectArgument.list)"
 70 |     />`);
 71 |   await page.getByTestId("button").click();
 72 |   await expect(page.getByTestId("button")).toHaveText("1,2,3");
 73 | });
 74 | 
 75 | test("For-loop in vars function works", async ({ page, initTestBed }) => {
 76 |   await initTestBed(`
 77 |     <Button
 78 |       testId="button"
 79 |       var.log=""
 80 |       var.runLoop="{()=>{ for (let i = 0; i < 10; i++) { log += i } }}"
 81 |       label="{log}"
 82 |       onClick="runLoop()"
 83 |     />`);
 84 |   await page.getByTestId("button").click();
 85 |   await expect(page.getByTestId("button")).toHaveText("0123456789");
 86 | });
 87 | 
 88 | test("Array.map works with the binding engine", async ({ page, initTestBed }) => {
 89 |   await initTestBed(`
 90 |     <Button
 91 |       testId="button"
 92 |       var.log=""
 93 |       var.items="{ [1, 2, 3] }"
 94 |       var.itemsMapped="{ items.map(id => { return { id: id } }) }"
 95 |       label="{log}"
 96 |       onClick="log = JSON.stringify(itemsMapped)"
 97 |     />`);
 98 |   await page.getByTestId("button").click();
 99 |   await expect(page.getByTestId("button")).toHaveText('[{"id":1},{"id":2},{"id":3}]');
100 | });
101 | 
102 | test("Compound components works", async ({ page }) => {
103 |   await initApp(page, {
104 |     name: "Compound component",
105 |     components: [
106 |       {
107 |         name: "AnotherTestButton",
108 |         component: {
109 |           type: "Button",
110 |           uid: "test button inner compound inner1",
111 |           props: {
112 |             debug: true,
113 |             label: "{$props.label}",
114 |           },
115 |           events: {
116 |             click: "emitEvent('click')",
117 |           },
118 |         },
119 |       },
120 |       {
121 |         name: "TestButton",
122 |         component: {
123 |           type: "Stack",
124 |           props: {
125 |             debug: true,
126 |           },
127 |           children: [
128 |             {
129 |               type: "Button",
130 |               uid: "test button inner1",
131 |               testId: "button1",
132 |               props: {
133 |                 debug: true,
134 |                 label: "{$props.label1}",
135 |               },
136 |               events: {
137 |                 click: "emitEvent('click1')",
138 |               },
139 |             },
140 |             {
141 |               type: "Button",
142 |               testId: "button2",
143 |               uid: "test button inner2",
144 |               props: {
145 |                 debug: true,
146 |                 label: "{$props.label2}",
147 |               },
148 |               events: {
149 |                 click: "emitEvent('click2')",
150 |               },
151 |             },
152 |             {
153 |               type: "AnotherTestButton",
154 |               testId: "button3",
155 |               uid: "test button inner compound",
156 |               props: {
157 |                 debug: true,
158 |                 label: "{$props.label3}",
159 |               },
160 |               events: {
161 |                 click: "emitEvent('click3')",
162 |               },
163 |             },
164 |           ],
165 |         },
166 |       },
167 |     ],
168 |     entryPoint: {
169 |       type: "TestButton",
170 |       vars: {
171 |         counter1: 0,
172 |         counter2: 0,
173 |         counter3: 0,
174 |       },
175 |       uid: "test button used",
176 |       props: {
177 |         debug: true,
178 |         label1: "Button1 {counter1}",
179 |         label2: "Button2 {counter2}",
180 |         label3: "Button3 {counter3}",
181 |       },
182 |       events: {
183 |         click1: "counter1++",
184 |         click2: "counter2++",
185 |         click3: "counter3++",
186 |       },
187 |     },
188 |   });
189 |   await page.getByTestId("button1").click();
190 |   await page.getByTestId("button1").click();
191 |   await page.getByTestId("button1").click();
192 |   await expect(page.getByTestId("button1")).toHaveText("Button1 3");
193 |   await page.getByTestId("button2").click();
194 |   await page.getByTestId("button2").click();
195 |   await expect(page.getByTestId("button2")).toHaveText("Button2 2");
196 |   await page.getByTestId("button3").click();
197 |   await expect(page.getByTestId("button3")).toHaveText("Button3 1");
198 | });
199 | 
200 | test("Arrow in arrow works", async ({ page, initTestBed }) => {
201 |   await initTestBed(`
202 |     <Stack
203 |       var.current="{0}"
204 |       var.total="{200}"
205 |       var.progressFn="{(cur, tot) => (Math.floor(1000 * cur / tot)) / 10}"
206 |       var.progressLabel="{(progressCalc, cur, tot) => 'Progress: ' + progressCalc(cur, tot) + '%'}"
207 |     >
208 |       <Button testId="button" label="Process with next item" onClick="current++" />
209 |       <Text testId="text" value="{progressLabel(progressFn, current, total)}" />
210 |     </Stack>`);
211 |   await page.getByTestId("button").click();
212 |   await expect(page.getByTestId("text")).toHaveText("Progress: 0.5%");
213 |   await page.getByTestId("button").click();
214 |   await expect(page.getByTestId("text")).toHaveText("Progress: 1%");
215 | });
216 | 
217 | test("Recursive arrow function works", async ({ page, initTestBed }) => {
218 |   await initTestBed(`
219 |     <Button testId="button" var.label="" var.items="{[1,2,3]}" label="{label}"
220 |       onClick="const fact = n => { if (n === 0) return 1; else return n * fact(n-1); }; label = fact(6);"
221 |     />`);
222 |   await page.getByTestId("button").click();
223 |   await expect(page.getByTestId("button")).toHaveText("720");
224 | });
225 | 
226 | test("Array.map works in an event #1", async ({ page, initTestBed }) => {
227 |   await initTestBed(`
228 |     <Button
229 |       testId="button"
230 |       var.label=""
231 |       label="{label}"
232 |       onClick="label = JSON.stringify([1,2,3].map(id => {return {id: id} }));"
233 |     />`);
234 |   await page.getByTestId("button").click();
235 |   await expect(page.getByTestId("button")).toHaveText('[{"id":1},{"id":2},{"id":3}]');
236 | });
237 | 
238 | test("Array.map works in an event #2", async ({ page, initTestBed }) => {
239 |   await initTestBed(`
240 |     <Button
241 |       testId="button"
242 |       var.label=""
243 |       props.label="{label}"
244 |       onClick="label = JSON.stringify([[1],[2,3],[4,5,6]].map(item => {return item.map(id => { return {id: id}; })}))"
245 |     />`);
246 |   await page.getByTestId("button").click();
247 |   await expect(page.getByTestId("button")).toHaveText(
248 |     '[[{"id":1}],[{"id":2},{"id":3}],[{"id":4},{"id":5},{"id":6}]]',
249 |   );
250 | });
251 | 
252 | test("Array.map works in an event #3", async ({ page, initTestBed }) => {
253 |   await initTestBed(`
254 |     <Button
255 |       testId="button"
256 |       var.label=""
257 |       label="{label}"
258 |       onClick="label = JSON.stringify([1,2,3,4,5,6].map(item => ({item})));"
259 |     />`);
260 |   await page.getByTestId("button").click();
261 |   await expect(page.getByTestId("button")).toHaveText(
262 |     '[{"item":1},{"item":2},{"item":3},{"item":4},{"item":5},{"item":6}]',
263 |   );
264 | });
265 | 
266 | test("Access var in parent container", async ({ page, initTestBed }) => {
267 |   await initTestBed(`
268 |     <Fragment var.isOpen="{false}">
269 |       <Text testId="text" value="isOpen: {isOpen}" />
270 |       <Fragment>
271 |         <Button testId="button1" label="toggle isOpen: {isOpen}" onClick="isOpen = !isOpen" />
272 |         <Button testId="button2" label="other toggle (in another nested container) isOpen: {isOpen}"
273 |           onClick="isOpen = !isOpen" />
274 |       </Fragment>
275 |     </Fragment>`);
276 |   const text = page.getByTestId("text");
277 |   const button1 = page.getByTestId("button1");
278 |   const button2 = page.getByTestId("button2");
279 | 
280 |   await expect(text).toHaveText("isOpen: false");
281 |   await button1.click();
282 |   await expect(text).toHaveText("isOpen: true");
283 |   await button2.click();
284 |   await expect(text).toHaveText("isOpen: false");
285 |   await button2.click();
286 |   await expect(text).toHaveText("isOpen: true");
287 |   await button1.click();
288 |   await expect(text).toHaveText("isOpen: false");
289 | });
290 | 
291 | test("Setting null-values variable works", async ({ page, initTestBed }) => {
292 |   await initTestBed(`
293 |     <Button testId="button" label="{stuff}" var.stuff="{null}" onClick="stuff = 0" />
294 |   `);
295 |   await page.getByTestId("button").click();
296 |   await expect(page.getByTestId("button")).toHaveText("0");
297 | });
298 | 
299 | test("Async hell", async ({ page }) => {
300 |   await initApp(page, {
301 |     entryPoint: {
302 |       type: "Stack",
303 |       vars: {
304 |         asyncValue: {
305 |           valami: {
306 |             counter: 0,
307 |             counter2: 0,
308 |           },
309 |         },
310 |         stuff: {
311 |           a: {
312 |             b: {
313 |               c: 0,
314 |             },
315 |           },
316 |         },
317 |         results: {},
318 | 
319 |         getResult: "{()=> { delay(100); return asyncValue.valami.counter} }",
320 |         set1: `{()=> {
321 |       if( !results['set1'] ){ results['set1'] = {}; }
322 |       results['set1'].setting = true;
323 |       asyncValue.valami.counter += 1;
324 |       results['set1'].value = asyncValue.valami.counter; 
325 |       results['set1'].setting = false;
326 |      }}`,
327 |         set2: `{()=> {
328 |       if(!results['set2']){ results['set2'] = {}; };
329 |       results['set2'].setting = true;
330 |       const res = getResult();
331 |       asyncValue.valami.counter2 += res; 
332 |       results['set2'].value = asyncValue.valami.counter2; 
333 |       results['set2'].setting = false;
334 |     }}`,
335 |       },
336 |       children: [
337 |         {
338 |           type: "Text",
339 |           testId: "text1",
340 |           props: {
341 |             value: "{JSON.stringify(asyncValue.valami)}",
342 |           },
343 |         },
344 |         {
345 |           type: "Text",
346 |           testId: "text2",
347 |           props: {
348 |             value: "{JSON.stringify(results)}",
349 |           },
350 |         },
351 |         {
352 |           type: "Stack",
353 |           children: [
354 |             {
355 |               type: "Button",
356 |               testId: "button1",
357 |               props: {},
358 |               events: {
359 |                 click: "set1();",
360 |               },
361 |             },
362 |           ],
363 |           events: {
364 |             mounted: "set1()",
365 |           },
366 |         },
367 |         {
368 |           type: "Stack",
369 |           children: [
370 |             {
371 |               type: "Button",
372 |               testId: "button2",
373 |               props: {},
374 |               events: {
375 |                 click: "set2();",
376 |               },
377 |             },
378 |           ],
379 |           events: {
380 |             mounted: "set2()",
381 |           },
382 |         },
383 |       ],
384 |     },
385 |   });
386 | 
387 |   await expect(page.getByTestId("text1")).toHaveText('{"counter":1,"counter2":1}');
388 |   await expect(page.getByTestId("text2")).toHaveText(
389 |     '{"set1":{"setting":false,"value":1},"set2":{"setting":false,"value":1}}'
390 |   );
391 |   await page.getByTestId("button1").click();
392 |   await delay(200);
393 |   await expect(page.getByTestId("text1")).toHaveText('{"counter":2,"counter2":1}');
394 |   await expect(page.getByTestId("text2")).toHaveText(
395 |     '{"set1":{"setting":false,"value":2},"set2":{"setting":false,"value":1}}'
396 |   );
397 |   await page.getByTestId("button2").click();
398 |   await delay(200);
399 |   await expect(page.getByTestId("text1")).toHaveText('{"counter":2,"counter2":3}');
400 |   await expect(page.getByTestId("text2")).toHaveText(
401 |     '{"set1":{"setting":false,"value":2},"set2":{"setting":false,"value":3}}'
402 |   );
403 | });
404 | 
405 | test("Async return value works", async ({ page, initTestBed }) => {
406 |   await initTestBed(`
407 |     <Stack
408 |       var.counter="{0}"
409 |       var.getResult="{()=>{ delay(100); return 4; }}"
410 |       var.setAsync="{()=> { counter += getResult();  }}"
411 |       var.setAsyncWORKS="{()=> { const result = getResult(); counter += result;  }}"
412 |     >
413 |       <Text testId="text" value="{counter}" />
414 |       <Button testId="button1" label="Doesn't work" onClick="setAsync()" />
415 |       <Button testId="button2" label="Works" onClick="setAsyncWORKS()" />
416 |     </Stack>`);
417 |   await page.getByTestId("button1").click();
418 |   await expect(page.getByTestId("text")).toHaveText("4");
419 |   await page.getByTestId("button2").click();
420 |   await expect(page.getByTestId("text")).toHaveText("8");
421 | });
422 | 
423 | test("Array push direct/indirect works", async ({ page, initTestBed }) => {
424 |   await initTestBed(`
425 |     <Stack
426 |       var.array="{[]}"
427 |       var.objectWithArray="{ { arrayInside: [] } }"
428 |     >
429 |       <Text testId="text1" value="{array}" />
430 |       <Text testId="text2" value="{objectWithArray.arrayInside}" />
431 |       
432 |       <Button testId="button1" label="Push to array" onClick="array.push('hello')" />
433 |       
434 |       <Button testId="button2" label="Push to array in object"
435 |         onClick="objectWithArray.arrayInside.push('hello')" />
436 |       
437 |       <Button testId="button3" label="Push to array (indirect)"
438 |         onClick="const n = array; n.push('hello')" />
439 |       
440 |       <Button testId="button4" label="Push to array in object (indirect)" 
441 |         onClick="const n = objectWithArray; n.arrayInside.push('hello')" />
442 |     </Stack>`);
443 |   await page.getByTestId("button1").click();
444 |   await expect(page.getByTestId("text1")).toHaveText("hello");
445 |   await expect(page.getByTestId("text2")).toHaveText("");
446 |   await page.getByTestId("button2").click();
447 |   await expect(page.getByTestId("text1")).toHaveText("hello");
448 |   await expect(page.getByTestId("text2")).toHaveText("hello");
449 |   await page.getByTestId("button3").click();
450 |   await expect(page.getByTestId("text1")).toHaveText("hello,hello");
451 |   await expect(page.getByTestId("text2")).toHaveText("hello");
452 |   await page.getByTestId("button4").click();
453 |   await expect(page.getByTestId("text1")).toHaveText("hello,hello");
454 |   await expect(page.getByTestId("text2")).toHaveText("hello,hello");
455 | });
456 | 
457 | test("Cannot write read-only var", async ({ page, initTestBed }) => {
458 |   await initTestBed(`
459 |     <Stack
460 |       var.msg=""
461 |       var.$counter="{ { value: 0 } }"
462 |       var.incrementFunc="{x => { try { x.value++; } catch { msg += ' read-only' } } }"
463 |     >
464 |       <Button
465 |         testId="button1"
466 |         label="{$counter.value}{msg}"
467 |         onClick="incrementFunc($counter)"
468 |       />
469 |       <Button
470 |         testId="button2"
471 |         label="{$counter.value} {msg}"
472 |         onClick="const n = $counter; incrementFunc(n)"
473 |       />
474 |     </Stack>`);
475 |   await expect(page.getByTestId("button1")).toHaveText("0");
476 |   await page.getByTestId("button1").click();
477 |   await expect(page.getByTestId("button1")).toHaveText("0 read-only");
478 |   await expect(page.getByTestId("button2")).toHaveText("0 read-only");
479 |   await page.getByTestId("button2").click();
480 |   await expect(page.getByTestId("button2")).toHaveText("0 read-only read-only");
481 | });
482 | 
483 | test("Event body transform to arrow works", async ({ page, initTestBed }) => {
484 |   await initTestBed(`
485 |     <Stack>
486 |       <Button testId="button1" var.val="{0}" label="#1 {val}" onClick="val = 23" />
487 |       <Button testId="button2" var.val="{0}" label="#2 {val}" onClick="{val = 23}" />
488 |       <Button testId="button3" var.val="{0}" label="#3 {val}" onClick="() => val = 23"/>
489 |       <Button testId="button4" var.val="{0}" label="#4 {val}" onClick="() => {val = 23}" />
490 |       <Button testId="button5" var.val="{0}" label="#5 {val}" onClick="(args) => val = args.toString()" />
491 |       <Button testId="button6" var.val="{0}" label="#6 {val}" onClick="{(args) => val = args.toString()}" />
492 |     </Stack>`);
493 |   const button1 = page.getByTestId("button1");
494 |   const button2 = page.getByTestId("button2");
495 |   const button3 = page.getByTestId("button3");
496 |   const button4 = page.getByTestId("button4");
497 |   const button5 = page.getByTestId("button5");
498 |   const button6 = page.getByTestId("button6");
499 | 
500 |   await expect(button1).toHaveText("#1 0");
501 |   await button1.click();
502 |   await expect(button1).toHaveText("#1 23");
503 |   await expect(button2).toHaveText("#2 0");
504 |   await button2.click();
505 |   await expect(button2).toHaveText("#2 23");
506 |   await expect(button3).toHaveText("#3 0");
507 |   await button3.click();
508 |   await expect(button3).toHaveText("#3 23");
509 |   await expect(button4).toHaveText("#4 0");
510 |   await button4.click();
511 |   await expect(button4).toHaveText("#4 23");
512 |   await expect(button5).toHaveText("#5 0");
513 |   await button5.click();
514 |   await expect(button5).toHaveText("#5 [object Object]");
515 |   await expect(button6).toHaveText("#6 0");
516 |   await button6.click();
517 |   await expect(button6).toHaveText("#6 [object Object]");
518 | });
519 | 
520 | async function delay(ms: number): Promise<void> {
521 |   return new Promise((r) => setTimeout(r, ms));
522 | }
523 | 
```
Page 79/189FirstPrevNextLast