#
tokens: 49534/50000 8/1628 files (page 44/141)
lines: off (toggle) GitHub
raw markdown copy
This is page 44 of 141. Use http://codebase.md/xmlui-org/xmlui/xmlui-standalone.umd.js?lines=false&page={x} to view the full context.

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/xmlui/dev-docs/next/configuration-management-enhancement-summary.md:
--------------------------------------------------------------------------------

```markdown
# Configuration Management Enhancement - Implementation Summary

## Overview
This document summarizes the implementation of enhanced configuration management for the XMLUI documentation generation scripts. This refactoring addressed scattered configuration loading patterns, added schema validation, standardized path resolution, and created a comprehensive configuration management system.

## Problems Addressed

### 1. Inconsistent Configuration Loading
**Before:** Each script manually loaded configuration files with inconsistent error handling and path resolution.
- Basic JSON parsing without validation
- Hard-coded path construction using `join()`
- Inconsistent error messages and handling
- No schema validation or type checking

**After:** Centralized configuration management with comprehensive features.

### 2. Hard-coded Path Construction
**Before:** Path construction scattered throughout scripts using manual `join()` calls.
- Magic string paths embedded in scripts
- No standardized base path resolution
- Project structure dependencies hard-coded

**After:** Intelligent path resolution with multiple strategies and standardized output locations.

### 3. No Configuration Validation
**Before:** No validation of configuration file contents.
- Runtime errors on invalid configurations
- No type checking or schema validation
- Unclear error messages for configuration issues

**After:** Comprehensive schema validation with clear error messages.

## Enhanced Configuration Management System

### Core Components

#### 1. **ConfigurationManager Class**
Centralized configuration loading with advanced features:
- **Schema validation** against predefined schemas
- **Search path resolution** - automatic file discovery
- **Error handling** with clear, actionable messages
- **Transformation support** for data preprocessing
- **Environment variable integration** for overrides

#### 2. **PathResolver Class**
Intelligent path resolution system:
- **Project root detection** - automatically finds project base
- **Multiple resolution strategies** - script, project, cwd relative
- **Standard output paths** - predefined locations for generated content
- **Cross-platform compatibility** - handles Windows/Unix path differences

#### 3. **ConfigValidator Class**
Comprehensive validation system:
- **Schema-based validation** with detailed error messages
- **Type checking** for properties and nested values
- **Allowed values validation** for enums and constrained fields
- **Default value application** for missing optional properties

### Configuration Schemas

#### **Components Configuration Schema:**
```javascript
{
  required: ["excludeComponentStatuses"],
  optional: ["includeInternalComponents", "sortOrder", "customTemplates"],
  properties: {
    excludeComponentStatuses: {
      type: "array",
      itemType: "string",
      allowedValues: ["internal", "experimental", "deprecated", "stable"]
    },
    includeInternalComponents: { type: "boolean", default: false },
    sortOrder: { 
      type: "string", 
      allowedValues: ["alphabetical", "status", "category"], 
      default: "alphabetical" 
    }
  }
}
```

#### **Extensions Configuration Schema:**
```javascript
{
  required: ["packageNames"],
  optional: ["excludePackages", "includeDevPackages"],
  properties: {
    packageNames: { type: "array", itemType: "string" },
    excludePackages: { type: "array", itemType: "string", default: [] },
    includeDevPackages: { type: "boolean", default: false }
  }
}
```

#### **Documentation Generator Schema:**
```javascript
{
  required: ["outputFormat"],
  optional: ["includeExamples", "generateMetadata", "verboseLogging"],
  properties: {
    outputFormat: { 
      type: "string", 
      allowedValues: ["markdown", "html", "json"], 
      default: "markdown" 
    },
    includeExamples: { type: "boolean", default: true },
    generateMetadata: { type: "boolean", default: true },
    verboseLogging: { type: "boolean", default: false }
  }
}
```

### Path Resolution Features

#### **Smart Search Paths:**
The system automatically searches for configuration files in:
1. Current working directory (`./`)
2. Script directory (`./scripts/generate-docs/`)
3. Docs config directory (`./docs/config/`)
4. Project root config (`./config/`)

#### **Standard Output Paths:**
Centralized definition of output locations:
```javascript
{
  themes: "dist/themes",
  components: "docs/content/components", 
  extensions: "docs/content/extensions",
  pages: "docs/pages",
  metadata: "dist/metadata",
  downloads: "docs/public/downloads"
}
```

#### **Flexible Resolution Strategies:**
- `script` - Relative to script directory
- `project` - Relative to project root
- `cwd` - Relative to current working directory

## Scripts Updated

### 1. **create-theme-files.mjs**
**Changes:**
- Uses `pathResolver.getOutputPaths().themes` instead of manual path construction
- Leverages enhanced configuration management for future theme configuration needs

**Benefits:**
- Automatic output directory resolution
- Consistent with other scripts' path handling
- Prepared for future theme configuration enhancements

### 2. **get-docs.mjs**
**Changes:**
- Uses `configManager.loadComponentsConfig()` and `configManager.loadExtensionsConfig()`
- Uses `pathResolver.resolvePath()` for component and example folder paths
- Consistent configuration loading with validation

**Benefits:**
- Automatic schema validation for configuration files
- Standardized error handling for configuration issues
- Simplified path construction logic

### 3. **input-handler.mjs**
**Changes:**
- Refactored to use enhanced configuration manager as backend
- Maintains backward compatibility with existing API
- Enhanced error handling and validation

**Benefits:**
- Improved error messages and validation
- Consistent behavior with other configuration loading
- Future-proof for additional configuration features

## Features and Benefits

### **Enhanced Error Handling**
- **Clear error messages** with specific information about what went wrong
- **File not found handling** with search path information
- **JSON syntax error detection** with line number information when possible
- **Permission error handling** for configuration files

### **Configuration Validation**
- **Schema-based validation** ensures configuration files are correct
- **Type checking** prevents runtime errors from wrong data types
- **Allowed values validation** for enumerated fields
- **Required property checking** with clear error messages

### **Path Resolution Intelligence**
- **Automatic project root detection** by looking for package.json
- **Multiple search strategies** for finding configuration files
- **Cross-platform path handling** for Windows/Unix compatibility
- **Standardized output directories** across all scripts

### **Developer Experience Improvements**
- **Detailed logging** for configuration loading process
- **Environment variable overrides** for CI/CD and development
- **Default value application** for missing optional settings
- **Backward compatibility** with existing configuration files

## Usage Examples

### **Basic Configuration Loading:**
```javascript
import { configManager } from "./configuration-management.mjs";

// Load and validate components configuration
const config = await configManager.loadComponentsConfig();

// Load with custom path and validation
const customConfig = await configManager.loadConfig("./custom-config.json", "COMPONENTS");
```

### **Path Resolution:**
```javascript
import { pathResolver } from "./configuration-management.mjs";

// Get standard output paths
const outputs = pathResolver.getOutputPaths();
const themeDir = outputs.themes;

// Resolve custom paths
const componentDir = pathResolver.resolvePath("src/components", "project");
const scriptFile = pathResolver.resolvePath("./script.mjs", "script");
```

### **Environment Integration:**
```javascript
// Override configuration with environment variables
const config = configManager.mergeWithEnvironment(baseConfig, "XMLUI_DOCS_");
// XMLUI_DOCS_VERBOSE_LOGGING=true becomes config.verboseLogging = true
```

## Validation Results

All enhanced configuration management features have been thoroughly tested:

### **Functionality Testing**
- ✅ `npm run export-themes` - Works with new path resolution
- ✅ Configuration loading - Handles missing files gracefully
- ✅ Schema validation - Rejects invalid configurations with clear errors
- ✅ Path resolution - Correctly resolves all path types

### **Error Handling Testing**
- ✅ **Missing files** - Clear error messages with search paths
- ✅ **Invalid JSON** - Syntax error reporting with file information
- ✅ **Schema violations** - Detailed validation error messages
- ✅ **Permission errors** - Appropriate error handling

### **Backward Compatibility**
- ✅ **Existing configurations** - All current config files work unchanged
- ✅ **API compatibility** - `loadConfig()` function maintains same interface
- ✅ **Output consistency** - Generated files identical to pre-refactoring

## Performance and Resource Impact

### **Performance Characteristics**
- **No performance degradation** - Configuration loading is still fast
- **Caching opportunities** - Path resolution results can be cached
- **Memory efficiency** - No significant memory overhead
- **Startup time** - Minimal impact on script startup time

### **Resource Usage**
- **File system** - More intelligent file searching reduces redundant operations
- **Error recovery** - Better error handling reduces failed script runs
- **Development time** - Significantly reduced debugging time for configuration issues

## Future Enhancement Opportunities

### **Phase 1 Extensions (Immediate)**
- Add configuration file hot-reloading for development
- Implement configuration merging from multiple sources
- Add JSON Schema file generation for IDE support

### **Phase 2 Extensions (Short-term)**
- YAML configuration file support
- Configuration templates and generators
- Advanced path aliasing and variable substitution

### **Phase 3 Extensions (Long-term)**
- Configuration management UI/CLI tools
- Integration with external configuration stores
- Advanced validation rules and custom validators

## Risk Assessment

### **Risk Mitigation Achieved**
- ✅ **Zero breaking changes** - Full backward compatibility maintained
- ✅ **Graceful degradation** - Missing configurations handled appropriately
- ✅ **Clear error messages** - Debugging and troubleshooting significantly improved
- ✅ **Comprehensive testing** - All functionality validated

### **Benefits vs. Risks**
- **High benefits** - Significantly improved maintainability and developer experience
- **Low risk** - No breaking changes, comprehensive testing
- **Future-proof** - Foundation for additional configuration enhancements
- **Immediate value** - Better error handling and validation right away

## Conclusion

The configuration management enhancement has successfully:

1. **Centralized configuration loading** with comprehensive error handling
2. **Added schema validation** for all configuration types
3. **Implemented intelligent path resolution** with multiple strategies
4. **Maintained full backward compatibility** while adding powerful new features
5. **Significantly improved developer experience** with better error messages and validation

The enhanced configuration management system provides a robust foundation for future configuration needs while immediately improving the reliability and maintainability of the documentation generation scripts. All changes are low-risk, well-tested, and provide immediate value to developers working with the system.

---
*Refactoring completed: 2024*
*Impact: High - significantly improved configuration reliability and developer experience*
*Risk: Low - no breaking changes, comprehensive validation*
*Status: Complete and validated* ✅

```

--------------------------------------------------------------------------------
/docs/content/components/Slider.md:
--------------------------------------------------------------------------------

```markdown
# Slider [#slider]

`Slider` provides an interactive control for selecting numeric values within a defined range, supporting both single value selection and range selection with multiple thumbs. It offers precise control through customizable steps and visual feedback with formatted value display.

Hover over the component to see the tooltip with the current value. On mobile, tap the thumb to see the tooltip.

**Key features:**
- **Range selection**: Single value or dual-thumb range selection with configurable minimum separation
- **Step control**: Precise incremental selection with customizable step values
- **Value formatting**: Custom display formatting for current values and visual feedback

## Properties [#properties]

### `autoFocus` (default: false) [#autofocus-default-false]

If this property is set to `true`, the component gets the focus automatically when displayed.

### `enabled` (default: true) [#enabled-default-true]

This boolean property value indicates whether the component responds to user events (`true`) or not (`false`).

### `initialValue` [#initialvalue]

This property sets the component's initial value.

```xmlui-pg
<Slider initialValue="5" />
```

### `maxValue` (default: 10) [#maxvalue-default-10]

This property specifies the maximum value of the allowed input range.

```xmlui-pg
<Slider maxValue="30" />
```

### `minStepsBetweenThumbs` (default: 1) [#minstepsbetweenthumbs-default-1]

This property sets the minimum number of steps required between multiple thumbs on the slider, ensuring they maintain a specified distance.

### `minValue` (default: 0) [#minvalue-default-0]

This property specifies the minimum value of the allowed input range.

```xmlui-pg
<Slider minValue="10" />
```

### `rangeStyle` [#rangestyle]

This optional property allows you to apply custom styles to the range element of the slider.

### `readOnly` (default: false) [#readonly-default-false]

Set this property to `true` to disallow changing the component value.

### `required` (default: false) [#required-default-false]

Set this property to `true` to indicate it must have a value before submitting the containing form.

### `showValues` (default: true) [#showvalues-default-true]

This property controls whether the slider shows the current values of the thumbs.

### `step` (default: 1) [#step-default-1]

This property defines the increment value for the slider, determining the allowed intervals between selectable values.

### `thumbStyle` [#thumbstyle]

This optional property allows you to apply custom styles to the thumb elements of the slider.

### `validationStatus` (default: "none") [#validationstatus-default-none]

This property allows you to set the validation status of the input component.

Available values:

| Value | Description |
| --- | --- |
| `valid` | Visual indicator for an input that is accepted |
| `warning` | Visual indicator for an input that produced a warning |
| `error` | Visual indicator for an input that produced an error |

### `valueFormat` (default: "(value) => value.toString()") [#valueformat-default-value-value-tostring]

This property allows you to customize how the values are displayed.

## Events [#events]

### `didChange` [#didchange]

This event is triggered when value of Slider has changed.

### `gotFocus` [#gotfocus]

This event is triggered when the Slider has received the focus.

### `lostFocus` [#lostfocus]

This event is triggered when the Slider has lost the focus.

## Exposed Methods [#exposed-methods]

### `focus` [#focus]

This method sets the focus on the slider component.

**Signature**: `focus(): void`

### `setValue` [#setvalue]

This API sets the value of the `Slider`. You can use it to programmatically change the value.

**Signature**: `setValue(value: number | [number, number] | undefined): void`

- `value`: The new value to set. Can be a single value or an array of values for range sliders.

### `value` [#value]

This API retrieves the current value of the `Slider`. You can use it to get the value programmatically.

**Signature**: `get value(): number | [number, number] | undefined`

## Styling [#styling]

### Theme Variables [#theme-variables]

| Variable | Default Value (Light) | Default Value (Dark) |
| --- | --- | --- |
| [backgroundColor](../styles-and-themes/common-units/#color)-range-Slider | $color-primary | $color-primary |
| [backgroundColor](../styles-and-themes/common-units/#color)-range-Slider | $color-primary | $color-primary |
| [backgroundColor](../styles-and-themes/common-units/#color)-range-Slider--disabled | $color-surface-400 | $color-surface-800 |
| [backgroundColor](../styles-and-themes/common-units/#color)-range-Slider--disabled | $color-surface-400 | $color-surface-800 |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider | $color-primary-500 | $color-primary-400 |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider | $color-primary-500 | $color-primary-400 |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider--active | $color-primary-400 | $color-primary-400 |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider--active | $color-primary-400 | $color-primary-400 |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider--focus | $color-primary | $color-primary |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider--focus | $color-primary | $color-primary |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider--hover | $color-primary | $color-primary |
| [backgroundColor](../styles-and-themes/common-units/#color)-thumb-Slider--hover | $color-primary | $color-primary |
| [backgroundColor](../styles-and-themes/common-units/#color)-track-Slider | $color-surface-200 | $color-surface-200 |
| [backgroundColor](../styles-and-themes/common-units/#color)-track-Slider | $color-surface-200 | $color-surface-200 |
| [backgroundColor](../styles-and-themes/common-units/#color)-track-Slider--disabled | $color-surface-300 | $color-surface-600 |
| [backgroundColor](../styles-and-themes/common-units/#color)-track-Slider--disabled | $color-surface-300 | $color-surface-600 |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-default | transparent | transparent |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-default | transparent | transparent |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-default--focus | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-default--hover | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-error | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-error--focus | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-error--hover | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-success | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-success--focus | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-success--hover | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-warning | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-warning--focus | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-Slider-warning--hover | *none* | *none* |
| [borderColor](../styles-and-themes/common-units/#color)-thumb-Slider | $color-surface-50 | $color-surface-950 |
| [borderColor](../styles-and-themes/common-units/#color)-thumb-Slider | $color-surface-50 | $color-surface-950 |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Slider-default | $borderRadius | $borderRadius |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Slider-default | $borderRadius | $borderRadius |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Slider-error | *none* | *none* |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Slider-success | *none* | *none* |
| [borderRadius](../styles-and-themes/common-units/#border-rounding)-Slider-warning | *none* | *none* |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Slider-default | solid | solid |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Slider-default | solid | solid |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Slider-error | *none* | *none* |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Slider-success | *none* | *none* |
| [borderStyle](../styles-and-themes/common-units/#border-style)-Slider-warning | *none* | *none* |
| [borderStyle](../styles-and-themes/common-units/#border-style)-thumb-Slider | solid | solid |
| [borderStyle](../styles-and-themes/common-units/#border-style)-thumb-Slider | solid | solid |
| [borderWidth](../styles-and-themes/common-units/#size)-Slider-default | 0 | 0 |
| [borderWidth](../styles-and-themes/common-units/#size)-Slider-default | 0 | 0 |
| [borderWidth](../styles-and-themes/common-units/#size)-Slider-error | *none* | *none* |
| [borderWidth](../styles-and-themes/common-units/#size)-Slider-success | *none* | *none* |
| [borderWidth](../styles-and-themes/common-units/#size)-Slider-warning | *none* | *none* |
| [borderWidth](../styles-and-themes/common-units/#size)-thumb-Slider | 2px | 2px |
| [borderWidth](../styles-and-themes/common-units/#size)-thumb-Slider | 2px | 2px |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-default | none | none |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-default | none | none |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-default--focus | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-default--hover | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-error | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-error--focus | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-error--hover | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-success | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-success--focus | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-success--hover | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-warning | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-warning--focus | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-Slider-warning--hover | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider | *none* | *none* |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider--active | 0 0 0 6px rgb(from $color-primary r g b / 0.4) | 0 0 0 6px rgb(from $color-primary r g b / 0.4) |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider--active | 0 0 0 6px rgb(from $color-primary r g b / 0.4) | 0 0 0 6px rgb(from $color-primary r g b / 0.4) |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider--focus | 0 0 0 6px rgb(from $color-primary r g b / 0.4) | 0 0 0 6px rgb(from $color-primary r g b / 0.4) |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider--focus | 0 0 0 6px rgb(from $color-primary r g b / 0.4) | 0 0 0 6px rgb(from $color-primary r g b / 0.4) |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider--hover | 0 0 0 6px rgb(from $color-primary r g b / 0.4) | 0 0 0 6px rgb(from $color-primary r g b / 0.4) |
| [boxShadow](../styles-and-themes/common-units/#boxShadow)-thumb-Slider--hover | 0 0 0 6px rgb(from $color-primary r g b / 0.4) | 0 0 0 6px rgb(from $color-primary r g b / 0.4) |

```

--------------------------------------------------------------------------------
/xmlui/bin/build.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { cp, mkdir, rm, writeFile } from "fs/promises";
import * as dotenv from "dotenv";
import { existsSync } from "fs";
import * as glob from "glob";
import type { InlineConfig } from "vite";
import { build as viteBuild } from "vite";
import { getViteConfig } from "./viteConfig";
import * as fs from "node:fs";

type ApiInterceptorDefinition = any;
type CompoundComponentDef = any;
type ThemeDefinition = any;

// --- Set up the configuration
dotenv.config({ path: `${process.cwd()}/.env` });
dotenv.config({ path: `${process.cwd()}/.env.local`, override: true });

type StandaloneJsonConfig = {
  name: string;
  appGlobals?: Record<string, any>;
  entryPoint?: string;
  components?: string[];
  themes?: string[];
  defaultTheme?: string;
  resources?: Record<string, string>;
  apiInterceptor?: ApiInterceptorDefinition;
  resourceMap?: Record<string, string>;
};

async function getExportedJsObjects<T>(path: string): Promise<Array<T>> {
  return await new Promise((resolve, reject) => {
    glob.glob(`${process.cwd()}/${path}`, (err: any, matches = []) => {
      const modules = Promise.all(
        matches.map(async (file: string) => {
          return (await import(file)).default;
        })
      );
      resolve(modules);
    });
  });
}

async function convertResourcesDir(distRoot: string, flatDist: boolean, filePrefix: string) {
  if (!flatDist) {
    return undefined;
  }
  distRoot = distRoot.replaceAll("\\", "/");
  const resourcesDir = `${distRoot}/resources`;
  if (!existsSync(resourcesDir)) {
    return undefined;
  }
  const files = await new Promise<string[]>((resolve, reject) => {
    glob.glob(`${resourcesDir}/**/*`, (err: any, matches = []) => {
      resolve(matches);
    });
  });

  const ret: Record<string, string> = {};
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    if (fs.statSync(file).isDirectory()) {
      continue;
    }
    const relativePath = file.replace(`${distRoot}/`, "");
    const convertedResource = `${filePrefix}${relativePath.replaceAll("/", "_")}`;

    await cp(file, `${distRoot}/${convertedResource}`);
    ret[relativePath] = convertedResource;
  }
  return ret;
}

export function removeLeadingSlashForPath(path: string): string {
  if (path.startsWith("/")) {
    return path.substring(1);
  }
  return path;
}

type UEBuildOptions = {
  buildMode?: "CONFIG_ONLY" | "INLINE_ALL" | "ALL";
  flatDist?: boolean;
  withMock?: boolean;
  withHostingMetaFiles?: boolean;
  withRelativeRoot?: boolean;
};

export const build = async ({
  buildMode = "CONFIG_ONLY",
  flatDist = false,
  withMock = false,
  withHostingMetaFiles = false,
  withRelativeRoot = false,
}: UEBuildOptions) => {
  const flatDistUiPrefix = "ui_";
  console.log("Building with options:", {
    buildMode,
    flatDist,
    withMock,
    withHostingMetaFiles,
    withRelativeRoot,
  });

  await viteBuild({
    ...(await getViteConfig({
      flatDist,
      withRelativeRoot,
      flatDistUiPrefix,
    })),
    define: {
      "process.env.VITE_BUILD_MODE": JSON.stringify(buildMode),
      "process.env.VITE_DEV_MODE": false,
      "process.env.VITE_MOCK_ENABLED": withMock,
      "process.env.VITE_APP_VERSION": JSON.stringify(process.env.VITE_APP_VERSION),

      "process.env.VITE_USED_COMPONENTS_App": JSON.stringify(process.env.VITE_USED_COMPONENTS_App),
      "process.env.VITE_USED_COMPONENTS_Chart": JSON.stringify(process.env.VITE_USED_COMPONENTS_Chart),
      "process.env.VITE_USED_COMPONENTS_AppHeader": JSON.stringify(process.env.VITE_USED_COMPONENTS_AppHeader),
      "process.env.VITE_USED_COMPONENTS_Stack": JSON.stringify(process.env.VITE_USED_COMPONENTS_Stack),
      "process.env.VITE_USED_COMPONENTS_Link": JSON.stringify(process.env.VITE_USED_COMPONENTS_Link),
      "process.env.VITE_USED_COMPONENTS_Text": JSON.stringify(process.env.VITE_USED_COMPONENTS_Text),
      "process.env.VITE_USED_COMPONENTS_Button": JSON.stringify(process.env.VITE_USED_COMPONENTS_Button),
      "process.env.VITE_USED_COMPONENTS_Card": JSON.stringify(process.env.VITE_USED_COMPONENTS_Card),
      "process.env.VITE_USED_COMPONENTS_Image": JSON.stringify(process.env.VITE_USED_COMPONENTS_Image),
      "process.env.VITE_USED_COMPONENTS_Footer": JSON.stringify(process.env.VITE_USED_COMPONENTS_Footer),
      "process.env.VITE_USED_COMPONENTS_SpaceFiller": JSON.stringify(process.env.VITE_USED_COMPONENTS_SpaceFiller),
      "process.env.VITE_USED_COMPONENTS_Pdf": JSON.stringify(process.env.VITE_USED_COMPONENTS_Pdf),
      "process.env.VITE_USED_COMPONENTS_Textarea": JSON.stringify(process.env.VITE_USED_COMPONENTS_Textarea),
      "process.env.VITE_USED_COMPONENTS_Logo": JSON.stringify(process.env.VITE_USED_COMPONENTS_Logo),
      "process.env.VITE_USED_COMPONENTS_NavLink": JSON.stringify(process.env.VITE_USED_COMPONENTS_NavLink),
      "process.env.VITE_USED_COMPONENTS_NavGroup": JSON.stringify(process.env.VITE_USED_COMPONENTS_NavGroup),
      "process.env.VITE_USED_COMPONENTS_Form": JSON.stringify(process.env.VITE_USED_COMPONENTS_Form),
      "process.env.VITE_USED_COMPONENTS_Tree": JSON.stringify(process.env.VITE_USED_COMPONENTS_Tree),
      "process.env.VITE_USED_COMPONENTS_Checkbox": JSON.stringify(process.env.VITE_USED_COMPONENTS_Checkbox),
      "process.env.VITE_USED_COMPONENTS_Switch": JSON.stringify(process.env.VITE_USED_COMPONENTS_Switch),
      "process.env.VITE_USED_COMPONENTS_DotMenu": JSON.stringify(process.env.VITE_USED_COMPONENTS_DotMenu),
      "process.env.VITE_USED_COMPONENTS_Heading": JSON.stringify(process.env.VITE_USED_COMPONENTS_Heading),
      "process.env.VITE_USED_COMPONENTS_Fragment": JSON.stringify(process.env.VITE_USED_COMPONENTS_Fragment),
      "process.env.VITE_USED_COMPONENTS_Table": JSON.stringify(process.env.VITE_USED_COMPONENTS_Table),
      "process.env.VITE_USED_COMPONENTS_List": JSON.stringify(process.env.VITE_USED_COMPONENTS_List),
      "process.env.VITE_USED_COMPONENTS_XmluiCodeHightlighter": JSON.stringify(process.env.VITE_USED_COMPONENTS_XmluiCodeHightlighter),
      "process.env.VITE_USED_COMPONENTS_Charts": JSON.stringify(process.env.VITE_USED_COMPONENTS_Charts),
      "process.env.VITE_INCLUDE_HTML_COMPONENTS": JSON.stringify(process.env.VITE_INCLUDE_HTML_COMPONENTS),
      "process.env.VITE_USED_COMPONENTS_DatePicker": JSON.stringify(process.env.VITE_USED_COMPONENTS_DatePicker),
      "process.env.VITE_USED_COMPONENTS_NavPanel": JSON.stringify(process.env.VITE_USED_COMPONENTS_NavPanel),
      "process.env.VITE_USED_COMPONENTS_Pages": JSON.stringify(process.env.VITE_USED_COMPONENTS_Pages),
      "process.env.VITE_USED_COMPONENTS_StickyBox": JSON.stringify(process.env.VITE_USED_COMPONENTS_StickyBox),
      "process.env.VITE_USED_COMPONENTS_Badge": JSON.stringify(process.env.VITE_USED_COMPONENTS_Badge),
      "process.env.VITE_USED_COMPONENTS_Avatar": JSON.stringify(process.env.VITE_USED_COMPONENTS_Avatar),
      "process.env.VITE_USED_COMPONENTS_ContentSeparator": JSON.stringify(
        process.env.VITE_USED_COMPONENTS_ContentSeparator
      ),
      "process.env.VITE_USED_COMPONENTS_FlowLayout": JSON.stringify(process.env.VITE_USED_COMPONENTS_FlowLayout),
      "process.env.VITE_USED_COMPONENTS_ModalDialog": JSON.stringify(process.env.VITE_USED_COMPONENTS_ModalDialog),
      "process.env.VITE_USED_COMPONENTS_NoResult": JSON.stringify(process.env.VITE_USED_COMPONENTS_NoResult),
      "process.env.VITE_USED_COMPONENTS_Option": JSON.stringify(process.env.VITE_USED_COMPONENTS_Option),
      "process.env.VITE_USED_COMPONENTS_FileUploadDropZone": JSON.stringify(
        process.env.VITE_USED_COMPONENTS_FileUploadDropZone
      ),
      "process.env.VITE_USED_COMPONENTS_Icon": JSON.stringify(process.env.VITE_USED_COMPONENTS_Icon),
      "process.env.VITE_USED_COMPONENTS_Items": JSON.stringify(process.env.VITE_USED_COMPONENTS_Items),
      "process.env.VITE_USED_COMPONENTS_SelectionStore": JSON.stringify(
        process.env.VITE_USED_COMPONENTS_SelectionStore
      ),
      "process.env.VITE_INCLUDE_REST_COMPONENTS": JSON.stringify(process.env.VITE_INCLUDE_REST_COMPONENTS),
      "process.env.VITE_USED_COMPONENTS_EmojiSelector": JSON.stringify(process.env.VITE_USED_COMPONENTS_EmojiSelector),
    },
  } as InlineConfig);

  if (buildMode === "INLINE_ALL") {
    return;
  }
  const distPath = "/dist";
  const themesFolder = flatDist ? "" : "themes";
  const componentsFolder = flatDist ? "" : "components";
  const themesFolderPath = flatDist ? distPath : `${distPath}/${themesFolder}`;
  const componentsFolderPath = flatDist ? distPath : `${distPath}/${componentsFolder}`;

  function getThemeFileName(theme: ThemeDefinition) {
    return flatDist ? `${flatDistUiPrefix}theme_${theme.id}` : theme.id;
  }

  function getComponentFileName(component: CompoundComponentDef) {
    return flatDist ? `${flatDistUiPrefix}component_${component.name}` : component.name;
  }

  function getEntrypointFileName() {
    return flatDist ? `${flatDistUiPrefix}entrypoint` : `app`;
  }

  if (buildMode === "CONFIG_ONLY") {
    let appDef;
    try {
      appDef = (await import(process.cwd() + "/src/config")).default;
    } catch (e) {
      console.log("couldn't find config");
    }

    const themePaths: string[] = [];
    const themes = await getExportedJsObjects<ThemeDefinition>("/src/themes/**.*");
    const fullDistPath = `${process.cwd()}${distPath}`;
    if (themes) {
      for (const theme of themes) {
        if (!existsSync(process.cwd() + themesFolderPath)) {
          await mkdir(process.cwd() + themesFolderPath, { recursive: true });
        }
        await writeFile(
          `${process.cwd()}${themesFolderPath}/${getThemeFileName(theme)}.json`,
          JSON.stringify(theme, null, 4)
        );
        themePaths.push(removeLeadingSlashForPath(`${themesFolder}/${getThemeFileName(theme)}.json`));
      }
    }

    const configJson: StandaloneJsonConfig = {
      ...appDef,
      resourceMap: await convertResourcesDir(fullDistPath, flatDist, flatDistUiPrefix),
      themes: themePaths,
    };
    delete configJson["components"];
    delete configJson["entryPoint"];
    if (!withMock) {
      delete configJson.apiInterceptor;
    }
    await writeFile(`${process.cwd()}${distPath}/config.json`, JSON.stringify(configJson, null, 4));
  } else if (buildMode === "ALL") {
    // const componentPaths: string[] = [];
    //   const components = await getExportedJsObjects<CompoundComponentDef>("/src/components/**.*");
    //   if (components) {
    //     for (const component of components) {
    //       if (!existsSync(`${process.cwd()}${componentsFolderPath}`)) {
    //         await mkdir(`${process.cwd()}${componentsFolderPath}`, { recursive: true });
    //       }
    //       await writeFile(
    //           `${process.cwd()}${componentsFolderPath}/${getComponentFileName(component)}.json`,
    //           JSON.stringify(component, null, 4)
    //       );
    //       componentPaths.push(removeLeadingSlashForPath(`${componentsFolder}/${getComponentFileName(component)}.json`));
    //     }
    //   }
    //
    // await writeFile(
    //   `${process.cwd()}${distPath}/${getEntrypointFileName()}.json`,
    //   JSON.stringify(appDef.entryPoint, null, 4)
    // );
    //
    // configJson = {
    //   ...configJson,
    //   entryPoint: `${getEntrypointFileName()}.json`,
    //   components: componentPaths,
    // };
  }

  //TODO temp, we should enforce user(who writes the metadata)-added-resources to be put in the resources folder
  if (flatDist) {
    await rm(`${process.cwd()}${distPath}/resources`, { recursive: true, force: true });
  }
  if (!withMock) {
    try {
      await rm(`${process.cwd()}${distPath}/mockServiceWorker.js`);
    } catch (ignored) {}
  }
  if (!withHostingMetaFiles) {
    try {
      await rm(`${process.cwd()}${distPath}/serve.json`);
    } catch (ignored) {}

    try {
      await rm(`${process.cwd()}${distPath}/web.config`);
    } catch (ignored) {}
    try {
      await rm(`${process.cwd()}${distPath}/_redirects`);
    } catch (ignored) {}
  }
};

```

--------------------------------------------------------------------------------
/xmlui/src/components/Text/TextNative.tsx:
--------------------------------------------------------------------------------

```typescript
import type { CSSProperties } from "react";
import type React from "react";
import { forwardRef, useMemo, useRef, useCallback, useEffect, memo } from "react";
import { composeRefs } from "@radix-ui/react-compose-refs";
import classnames from "classnames";

import styles from "./Text.module.scss";

import { getMaxLinesStyle } from "../../components-core/utils/css-utils";
import {
  type BreakMode,
  type OverflowMode,
  type TextVariant,
  TextVariantElement,
} from "../abstractions";
import type { RegisterComponentApiFn } from "../..";
import { useComponentStyle } from "../../components-core/theming/StyleContext";
import { EMPTY_OBJECT } from "../../components-core/constants";
import { toCssVar } from "../../components-core/theming/layout-resolver";

// =============================================================================
// Custom Variant CSS Cache Infrastructure
// =============================================================================

/**
 * Cached CSS information for a custom variant.
 */
interface CustomVariantCacheEntry {
  /** The generated CSS class name for this variant */
  className: string;
  /** The CSS text content that defines the styles for this variant */
  cssText: string;
  /** Timestamp when this entry was created (for debugging/cleanup) */
  createdAt: number;
}

/**
 * Global cache that stores custom variant CSS styles.
 * Key: variant value (string)
 *
 * This cache ensures the same variant value always generates the same CSS.
 */
const customVariantCache = new Map<string, CustomVariantCacheEntry>();

/**
 * Retrieves a cached custom variant entry if it exists.
 */
export function getCustomVariantCache(
  variant: string,
): CustomVariantCacheEntry | undefined {
  return customVariantCache.get(variant);
}

/**
 * Stores a custom variant entry in the cache.
 */
export function setCustomVariantCache(
  variant: string,
  entry: Omit<CustomVariantCacheEntry, "createdAt">,
): void {
  customVariantCache.set(variant, {
    ...entry,
    createdAt: Date.now(),
  });
}

/**
 * Checks if a custom variant is already cached.
 */
export function hasCustomVariantCache(variant: string): boolean {
  return customVariantCache.has(variant);
}

/**
 * Clears the entire custom variant cache.
 * Useful for testing or full app resets.
 */
export function clearCustomVariantCache(): void {
  customVariantCache.clear();
}

/**
 * Gets cache statistics for debugging.
 */
export function getCustomVariantCacheStats() {
  return {
    totalEntries: customVariantCache.size,
    entries: Array.from(customVariantCache.entries()).map(([key, entry]) => ({
      key,
      className: entry.className,
      createdAt: new Date(entry.createdAt).toISOString(),
    })),
  };
}

// =============================================================================
// Component Definition
// =============================================================================

type TextProps = {
  uid?: string;
  children?: React.ReactNode;
  variant?: TextVariant;
  maxLines?: number;
  preserveLinebreaks?: boolean;
  ellipses?: boolean;
  overflowMode?: OverflowMode;
  breakMode?: BreakMode;
  style?: CSSProperties;
  className?: string;
  registerComponentApi?: RegisterComponentApiFn;
  [variantSpecificProps: string]: any;
};

export const defaultProps = {
  maxLines: 0,
  preserveLinebreaks: false,
  ellipses: true,
  overflowMode: undefined as OverflowMode | undefined,
  breakMode: "normal" as BreakMode | undefined,
};

export const Text = forwardRef(function Text(
  {
    uid,
    variant,
    maxLines = defaultProps.maxLines,
    style,
    className,
    children,
    preserveLinebreaks = defaultProps.preserveLinebreaks,
    ellipses = defaultProps.ellipses,
    overflowMode = defaultProps.overflowMode,
    breakMode = defaultProps.breakMode,
    registerComponentApi,
    ...variantSpecificProps
  }: TextProps,
  forwardedRef,
) {
  const innerRef = useRef<HTMLElement>(null);
  const ref = forwardedRef ? composeRefs(innerRef, forwardedRef) : innerRef;

  // Implement hasOverflow function
  const hasOverflow = useCallback((): boolean => {
    const element = innerRef.current;
    if (!element) return false;

    // Check both horizontal and vertical overflow
    const hasHorizontalOverflow = element.scrollWidth > element.clientWidth;
    const hasVerticalOverflow = element.scrollHeight > element.clientHeight;

    return hasHorizontalOverflow || hasVerticalOverflow;
  }, []);

  // Register API with XMLUI if provided
  useEffect(() => {
    if (registerComponentApi) {
      registerComponentApi({ hasOverflow });
    }
  }, [registerComponentApi, hasOverflow]);

  // NOTE: This is to accept syntax highlight classes coming from shiki
  // classes need not to be added to the rendered html element, so we remove them from props
  const { syntaxHighlightClasses, ...restVariantSpecificProps } = variantSpecificProps;

  const Element = useMemo(() => {
    if (!variant || !TextVariantElement[variant]) return "div";
    return TextVariantElement[variant];
  }, [variant]);

  // Custom variant CSS generation
  // Following React hook rules: hooks must be called unconditionally
  // We always call useComponentStyle, passing empty object for known variants
  const isCustomVariant = useMemo(() => {
    return variant && !TextVariantElement[variant];
  }, [variant]);

  // Always call useComponentStyle (React hook rule: no conditional hooks)
  // For now, pass empty object; later this will contain assembled CSS properties
  const variantSpec = useMemo(
    () => {
      if (!isCustomVariant) return EMPTY_OBJECT;
      const subject = `-Text-${variant}`;
      const cssInput = {
        color: toCssVar(`$textColor${subject}`),
        "font-family": toCssVar(`$fontFamily${subject}`),
        "font-size": toCssVar(`$fontSize${subject}`),
        "font-style": toCssVar(`$fontStyle${subject}`),
        "font-weight": toCssVar(`$fontWeight${subject}`),
        "font-stretch": toCssVar(`$fontStretch${subject}`),
        "text-decoration-line": toCssVar(`$textDecorationLine${subject}`),
        "text-decoration-color": toCssVar(`$textDecorationColor${subject}`),
        "text-decoration-style": toCssVar(`$textDecorationStyle${subject}`),
        "text-decoration-thickness": toCssVar(`$textDecorationThickness${subject}`),
        "text-underline-offset": toCssVar(`$textUnderlineOffset${subject}`),
        "line-height": toCssVar(`$lineHeight${subject}`),
        "background-color": toCssVar(`$backgroundColor${subject}`),
        "text-transform": toCssVar(`$textTransform${subject}`),
        "letter-spacing": toCssVar(`$letterSpacing${subject}`),
        "word-spacing": toCssVar(`$wordSpacing${subject}`),
        "text-shadow": toCssVar(`$textShadow${subject}`),
        "text-indent": toCssVar(`$textIndent${subject}`),
        "text-align": toCssVar(`$textAlign${subject}`),
        "text-align-last": toCssVar(`$textAlignLast${subject}`),
        "word-break": toCssVar(`$wordBreak${subject}`),
        "word-wrap": toCssVar(`$wordWrap${subject}`),
        direction: toCssVar(`$direction${subject}`),
        "writing-mode": toCssVar(`$writingMode${subject}`),
        "line-break": toCssVar(`$lineBreak${subject}`),
      };
      return cssInput;
    },
    [isCustomVariant, variant],
  );
  const customVariantClassName = useComponentStyle(variantSpec);

  // Store custom variant in cache if it's a new custom variant
  useEffect(() => {
    if (isCustomVariant && variant && customVariantClassName) {
      // Check if this variant is already cached
      if (!hasCustomVariantCache(variant)) {
        // TODO: When CSS generation is implemented, extract the actual CSS text
        // For now, store placeholder information
        setCustomVariantCache(variant, {
          className: customVariantClassName,
          cssText: "", // Will be populated when CSS generation is implemented
        });
      }
    }
  }, [isCustomVariant, variant, customVariantClassName]);

  // Determine overflow mode classes based on overflowMode and existing props
  const overflowClasses = useMemo(() => {
    const classes: Record<string, boolean> = {};

    // If overflowMode is not explicitly set, use original behavior
    if (!overflowMode) {
      classes[styles.truncateOverflow] = maxLines > 0;
      classes[styles.noEllipsis] = !ellipses;
      return classes;
    }

    switch (overflowMode) {
      case "none":
        // CSS: overflow: hidden + text-overflow: clip + normal wrapping
        // Effect: Text wraps normally but clips cleanly at container boundaries without ellipsis
        classes[styles.overflowNone] = true;
        break;
      case "scroll":
        // CSS: white-space: nowrap + overflow-x: auto + overflow-y: hidden
        // Effect: Forces single line, enables horizontal scrollbar when content overflows
        classes[styles.overflowScroll] = true;
        break;
      case "ellipsis":
        // CSS: Uses -webkit-line-clamp for multi-line or white-space: nowrap + text-overflow: ellipsis for single line
        // Effect: Shows "..." when text is truncated, respects maxLines for multi-line truncation
        classes[styles.truncateOverflow] = true;
        classes[styles.noEllipsis] = !ellipses;
        break;
      case "flow":
        // CSS: white-space: normal + overflow-y: auto + overflow-x: hidden
        // Effect: Text wraps to multiple lines with vertical scrolling when needed, no horizontal scrollbar
        // Note: Flow mode ignores maxLines to allow unlimited text wrapping
        classes[styles.overflowFlow] = true;
        break;
    }

    return classes;
  }, [overflowMode, maxLines, ellipses]);

  // Determine break mode classes
  const breakClasses = useMemo(() => {
    const classes: Record<string, boolean> = {};

    // Only apply break mode classes if explicitly set (preserves theme variable support)
    if (breakMode) {
      switch (breakMode) {
        case "normal":
          // CSS: word-break: normal + overflow-wrap: normal
          // Effect: Standard word breaking at natural boundaries (spaces, hyphens)
          classes[styles.breakNormal] = true;
          break;
        case "word":
          // CSS: overflow-wrap: break-word
          // Effect: Breaks long words only when necessary to prevent overflow, preserves word boundaries when possible
          classes[styles.breakWord] = true;
          break;
        case "anywhere":
          // CSS: word-break: break-all + overflow-wrap: anywhere
          // Effect: Most aggressive breaking - allows breaking between any characters to fit container
          classes[styles.breakAnywhere] = true;
          break;
        case "keep":
          // CSS: word-break: keep-all
          // Effect: Prevents breaking within words entirely (useful for CJK text or technical terms)
          classes[styles.breakKeep] = true;
          break;
        case "hyphenate":
          // CSS: hyphens: auto + overflow-wrap: break-word
          // Effect: Uses browser's hyphenation dictionary to break words with proper hyphens
          classes[styles.breakHyphenate] = true;
          break;
      }
    }

    return classes;
  }, [breakMode]);

  return (
    <Element
      {...restVariantSpecificProps}
      ref={ref as any}
      className={classnames(
        syntaxHighlightClasses,
        styles.text,
        // Use custom variant className if it's a custom variant, otherwise use predefined variant style
        isCustomVariant ? customVariantClassName : styles[variant || "default"],
        {
          [styles.preserveLinebreaks]: preserveLinebreaks,
          ...overflowClasses,
          ...breakClasses,
        },
        className,
      )}
      style={{
        ...style,
        // Apply maxLines style for "ellipsis" mode and default behavior
        // "none", "scroll", and "flow" modes ignore maxLines for predictable, reliable behavior
        ...(overflowMode === "ellipsis" || (!overflowMode && maxLines)
          ? getMaxLinesStyle(maxLines)
          : {}),
      }}
    >
      {children}
    </Element>
  );
});

```

--------------------------------------------------------------------------------
/xmlui/src/testing/component-test-helpers.ts:
--------------------------------------------------------------------------------

```typescript
import type { Locator } from "@playwright/test";

import { type ParserResult, xmlUiMarkupToComponent } from "../components-core/xmlui-parser";
import type { ComponentDef, CompoundComponentDef } from "../abstractions/ComponentDefs";

import chroma, { type Color } from "chroma-js";
import { ComponentDriver } from "./ComponentDrivers";

export type ThemeTestDesc = {
  themeVar: string;
  themeVarAsCSS: string;
  expected: string;
  dependsOnVars?: Record<string, string>;
};

export function mapObject<K extends (val: any) => any, V extends (val: string) => string | number>(
  obj: Record<string, any>,
  valFn: K = ((val) => val) as K,
  keyFn: V = ((val) => val) as V,
) {
  const newObject = {} as Record<ReturnType<V>, ReturnType<K>>;
  Object.entries(obj).forEach(([key, value]) => {
    newObject[keyFn(key)] = valFn(value);
  });
  return newObject;
}

export function getComponentTagName(locator: Locator) {
  return locator.evaluate((el) => el.tagName.toLowerCase());
}

export function parseComponentIfNecessary(
  rawComponent: ComponentDef<any> | CompoundComponentDef | string,
): ParserResult {
  if (typeof rawComponent === "string") {
    return xmlUiMarkupToComponent(rawComponent);
  }
  return {
    component: rawComponent,
    errors: [],
    erroneousCompoundComponentName: undefined,
  };
}

/**
 * Scales a value by a percentage, using NumericCSS for parsing and clarity.
 * @param scalarOf100Percent The value representing 100%.
 * @param percentage A percentage string in the format "NN%", e.g., "40%". Must end with '%'.
 * @returns The scaled value.
 */
export function scaleByPercent(scalarOf100Percent: number, percentage: string) {
  const parsed = parseAsNumericCss(percentage);
  if (parsed.unit !== "%") {
    throw new Error(`Expected percentage unit (%), got: ${parsed.unit}`);
  }
  return (scalarOf100Percent / 100) * parsed.value;
}

export function getBoundingRect(locator: Locator) {
  return locator.evaluate((element) => element.getBoundingClientRect());
}

export function getElementStyle(specifier: Locator, style: string) {
  return specifier.evaluate(
    (element, style) => window.getComputedStyle(element).getPropertyValue(style),
    style,
  );
}

/**
 * Retreives all the provided style properties from the locator
 * @returns an object with the keys being the elements of the styles argument
 */
export function getElementStyles(locator: Locator, styles: string[] = []) {
  return locator.evaluate(
    (element, styles) =>
      Object.fromEntries(
        styles.map((styleName) => [
          styleName,
          window.getComputedStyle(element).getPropertyValue(styleName),
        ]),
      ),
    styles,
  );
}

export function isIndeterminate(specifier: ComponentDriver | Locator) {
  if (specifier instanceof ComponentDriver) specifier = specifier.component;
  return specifier.evaluate((el: HTMLInputElement) => el.indeterminate);
}

export async function overflows(locator: Locator, direction: "x" | "y" | "both" = "both") {
  const [width, height, scrollWidth, scrollHeight] = await locator.evaluate((element) => [
    element.clientWidth,
    element.clientHeight,
    element.scrollWidth,
    element.scrollHeight,
  ]);
  if (direction === "x") return scrollWidth > width;
  if (direction === "y") return scrollHeight > height;
  return scrollWidth > width && scrollHeight > height;
}

// ----------------------------------
// ComponentDriver style helpers
// ----------------------------------

export function getStyles(specifier: ComponentDriver | Locator, style: string | string[]) {
  if (specifier instanceof ComponentDriver) specifier = specifier.component;
  style = Array.isArray(style) ? style : [style];
  return specifier.evaluate(
    (element, styles) =>
      Object.fromEntries(
        styles.map((styleName) => [
          styleName
            .trim()
            .split("-")
            .map((n, idx) => (idx === 0 ? n : n[0].toUpperCase() + n.slice(1)))
            .join(""),
          window.getComputedStyle(element).getPropertyValue(styleName),
        ]),
      ),
    style,
  );
}

export function getPseudoStyles(
  specifier: ComponentDriver | Locator,
  pseudoElement: string,
  style: string | string[],
) {
  if (specifier instanceof ComponentDriver) specifier = specifier.component;
  style = Array.isArray(style) ? style : [style];
  return specifier.evaluate(
    (element, obj) =>
      Object.fromEntries(
        obj.style.map((styleName) => [
          styleName
            .trim()
            .split("-")
            .map((n, idx) => (idx === 0 ? n : n[0].toUpperCase() + n.slice(1)))
            .join(""),
          window.getComputedStyle(element, obj.pseudoElement).getPropertyValue(styleName),
        ]),
      ),
    { style, pseudoElement },
  );
}

export async function getHtmlAttributes(
  specifier: ComponentDriver | Locator,
  attributes: string | string[],
): Promise<{ [k: string]: string }> {
  if (specifier instanceof ComponentDriver) specifier = specifier.component;
  attributes = Array.isArray(attributes) ? attributes : [attributes];

  const mapped = await Promise.all(
    attributes.map(async (attr) => {
      return [attr, await specifier.getAttribute(attr)];
    }),
  );
  return Object.fromEntries(mapped);
}

export async function getPaddings(specifier: ComponentDriver | Locator) {
  const paddings = mapObject(
    await getStyles(specifier, ["padding-left", "padding-right", "padding-top", "padding-bottom"]),
    parseAsNumericCss,
  );
  return {
    left: paddings.paddingLeft,
    right: paddings.paddingRight,
    top: paddings.paddingTop,
    bottom: paddings.paddingBottom,
  };
}

export async function getBorders(specifier: ComponentDriver | Locator) {
  const borders = mapObject(
    await getStyles(specifier, ["border-left", "border-right", "border-top", "border-bottom"]),
    parseAsCssBorder,
  );
  return {
    left: borders.borderLeft,
    right: borders.borderRight,
    top: borders.borderTop,
    bottom: borders.borderBottom,
  };
}

export function getMargins(specifier: ComponentDriver | Locator) {
  return getStyles(specifier, ["margin-left", "margin-right", "margin-top", "margin-bottom"]);
}

/**
 * Retrieves the bounding rectangle of the component including **margins** and **padding**
 * added to the dimensions.
 */
export async function getBounds(specifier: ComponentDriver | Locator) {
  if (specifier instanceof ComponentDriver) specifier = specifier.component;
  const boundingRect = await specifier.evaluate((element) => element.getBoundingClientRect());
  const m = mapObject(await getMargins(specifier), parseFloat);

  const width = boundingRect.width + m.marginLeft + m.marginRight;
  const height = boundingRect.height + m.marginTop + m.marginBottom;
  const left = boundingRect.left - m.marginLeft;
  const right = boundingRect.right + m.marginRight;
  const top = boundingRect.top - m.marginTop;
  const bottom = boundingRect.bottom + m.marginBottom;

  return {
    width,
    height,
    left,
    right,
    top,
    bottom,
  };
}

/**
 * Provides annotations for skipped tests and the ability to specify a reason.
 * Use it via the SKIP_REASON exported variable.
 */
class TestSkipReason {
  private addAnnotation(type: string, description?: string) {
    return {
      annotation: {
        type,
        description: description ?? "",
      },
    };
  }

  NOT_IMPLEMENTED_XMLUI(description?: string) {
    return this.addAnnotation("not implemented in xmlui", description);
  }

  TO_BE_IMPLEMENTED(description?: string) {
    return this.addAnnotation("to be implemented", description);
  }

  XMLUI_BUG(description?: string) {
    return this.addAnnotation("xmlui bug", description);
  }

  TEST_INFRA_BUG(description?: string) {
    return this.addAnnotation("test infra bug", description);
  }

  TEST_NOT_WORKING(description?: string) {
    return this.addAnnotation("test not working", description);
  }

  TEST_INFRA_NOT_IMPLEMENTED(description?: string) {
    return this.addAnnotation("test infra not implemented", description);
  }

  REFACTOR(description?: string) {
    return this.addAnnotation("refactor", description);
  }

  // Need to specify a reason here!
  UNSURE(description: string) {
    return this.addAnnotation("unsure", description);
  }
}

/**
 * Provides annotations for skipped tests and the ability to specify a reason.
 *
 * Usage:
 *
 * ```ts
 * import { SKIP_REASON } from "./tests/component-test-helpers";
 *
 * test.skip(
 *   "test name",
 *   SKIP_REASON.NOT_IMPLEMENTED_XMLUI("This test is not implemented in xmlui")
 *   async ({}) => {},
 * );
 * ```
 */
export const SKIP_REASON = new TestSkipReason();

// --- CSS types and parsers

export function pixelStrToNum(pixelStr: string) {
  return Number(pixelStr.replace("px", ""));
}

export function parseAsCssBorder(str: string) {
  const parts = str.split(/\s+(?=[a-z]+|\()/i);
  if (parts.length > 3) {
    throw new Error(`Provided value ${str} cannot be parsed as a CSS border`);
  }
  const style = parts.filter(isCSSBorderStyle);
  if (style.length > 1) {
    throw new Error(`Too many border styles provided in ${str}`);
  }

  const width = parts.filter(isNumericCSS);
  if (width.length > 1) {
    throw new Error(`Too many border widths provided in ${str}`);
  }

  const color = parts.filter((p) => chroma.valid(p));
  if (color.length > 1) {
    throw new Error(`Too many border colors provided in ${str}`);
  }

  const result: CSSBorder = {
    width: !!width[0] ? parseAsNumericCss(width[0]) : undefined,
    style: style[0],
    color: chroma(color[0]),
  };
  return result;
}

export interface NumericCSS {
  value: number;
  unit: CSSUnit;
}

const numericCSSRegex = /^([+-]?(?:\d+|\d*\.\d+))([a-z]*|%)$/;

export function isNumericCSS(str: any): str is NumericCSS {
  const parts = str.match(numericCSSRegex);

  if (!parts) return false;
  if (parts.length < 3) return false;
  if (isNaN(parseFloat(parts[1]))) return false;
  if (!isCSSUnit(parts[2])) return false;

  return true;
}

export function parseAsNumericCss(str: string) {
  const parts = str.match(numericCSSRegex);
  if (!parts) {
    throw new Error(`Provided value ${str} cannot be parsed as a numeric CSS value`);
  }
  if (parts.length < 3) {
    throw new Error(`${parts[0]} is not a correct numeric CSS value`);
  }
  const value = parseFloat(parts[1]);
  if (isNaN(value)) {
    throw new Error(`${value} is not a valid number in ${str}`);
  }
  const unit = parts[2];
  if (!isCSSUnit(unit)) {
    throw new Error(`${unit} is not have a valid CSS unit in ${str}`);
  }
  const result: NumericCSS = { value, unit };
  return result;
}

export function numericCSSToString(cssValue: NumericCSS) {
  return `${cssValue.value}${cssValue.unit}`;
}

export type CSSColor = chroma.Color;

export function isCSSColor(str: any): str is CSSColor {
  return chroma.valid(str);
}

export function parseAsCSSColor(str: string): CSSColor {
  return chroma(str);
}

const CSSUnitValues = [
  "px",
  "em",
  "rem",
  "vh",
  "vw",
  "%",
  "cm",
  "mm",
  "in",
  "pt",
  "pc",
  "ex",
  "ch",
  "vmin",
  "vmax",
] as const;
type CSSUnit = (typeof CSSUnitValues)[number];

function isCSSUnit(str: string): str is CSSUnit {
  return CSSUnitValues.includes(str as any);
}

export type CSSBorder = {
  width?: NumericCSS;
  style?: CSSBorderStyle;
  color?: Color;
};

const CSSBorderStyleValues = [
  "solid",
  "dotted",
  "dashed",
  "double",
  "none",
  "hidden",
  "groove",
  "ridge",
  "inset",
  "outset",
] as const;
export type CSSBorderStyle = (typeof CSSBorderStyleValues)[number];

export function isCSSBorderStyle(str: string): str is CSSBorderStyle {
  return CSSBorderStyleValues.includes(str as any);
}
export function parseAsCSSBorderStyle(str: string) {
  if (!isCSSBorderStyle(str)) {
    throw new Error(`Provided value ${str} cannot be parsed as a CSS border style`);
  }
  return str;
}

export const BorderSideValues = ["top", "bottom", "left", "right"] as const;
export type BorderSide = (typeof BorderSideValues)[number];

export function isBorderSide(str: string): str is BorderSide {
  return BorderSideValues.includes(str as any);
}

```

--------------------------------------------------------------------------------
/xmlui/src/components/Tree/Tree.md:
--------------------------------------------------------------------------------

```markdown
%-DESC-START

**Key features:**
- **Flat and hierarchical data structures**: You can select the most convenient data format to represent the tree. A set of properties enables you to map your data structure to the visual representation of the tree.
- **Flexible expand/collapse**: You have several properties to represent the expanded and collapsed state of tree nodes. You can also specify several options that determine which tree items are collapsed initially. 
- **Tree API**: Several exposed methods allow you to manage the tree's view state imperatively.

## Specifying Data

With the `dataFormat` property, you can select between "flat" or "hierarchy" formats. The component transforms the data according to the value of this property into a visual representation.

The "flat" and "hierarchy" data structures both use these fields for a particular tree node:
- `id`: Unique ID of tree node
- `name`: The field to be used as the display label
- `icon`: An optional icon identifier. If specified, this icon is displayed with the tree item.
- `iconExpanded`: An optional icon identifier. This icon is displayed when the field is expanded.
- `iconCollapsed`: An optional icon identifier. This icon is displayed when the field is collapsed.
- `selectable`: Indicates if the node can be selected.

The "flat" structure refers to its direct parent node via the `parentId` property, which contains the ID of the node it is referring to.

The "hierarchy" structure uses a `children` property, which is an array of nested child nodes (using the common node property set above).

This example demonstrates the use of the "flat" data mode:

```xmlui-pg display copy height="220px" /"flat"/ /parentId/ name="Example: flat data format"
<App>
  <Tree
    testId="tree"
    dataFormat="flat"
    defaultExpanded="all"
    data='{[
      { id: 1, icon:"folder", name: "Root Item 1", parentId: null },
      { id: 2, icon:"folder", name: "Child Item 1.1", parentId: 1 },
      { id: 3, icon: "code", name: "Child Item 1.2", parentId: 1 },
      { id: 4, icon: "code", name: "Grandchild Item 1.1.1", parentId: 2 },
    ]}'>
  </Tree>
</App>
```

This example demonstrates the use of the "hiearchy" data mode:

```xmlui-pg display copy height="220px" /"flat"/ /children/ name="Example: hierarchical data format"
<App>
  <Tree
    testId="tree"
    dataFormat="hierarchy"
    defaultExpanded="all"
    data='{[
      {
        id: 1, icon: "folder", name: "Root Item 1",
        children: [
          { id: 2, icon: "code", name: "Child Item 1.1" },
          { id: 3, icon: "folder", name: "Child Item 1.2",
            children: [
              { id: 4, icon: "code", name: "Grandchild Item 1.2.1"}
            ],
          }
        ],
      },
    ]}'>
  </Tree>
</App>
```

When you use data (for example, retrieved from a backend), those structures may use different property names. The `Tree` component allows mapping data field names through these properties: 
- `idField` (default: `id`)
- `nameField` (default: `name`)
- `iconField`  (default: `icon`)
- `iconExpandedField` (default: `iconExpanded`)
- `iconCollapsedField` (default: `iconCollapsed`)
- `parentIdField` (default: `parentId`)
- `childrenField` (default: `children`)
- `selectableField` (default: `selectable`)

The following example uses the `idField`, `nameField`, and `parentIdField` mapping properties:

```xmlui-pg display copy height="220px" /idField/ /nameField/ /parentIdField/ /uid/ /label/ /parent/ name="Example: mapping data fields"
<App>
  <Tree
    testId="tree"
    dataFormat="flat"
    defaultExpanded="all"
    idField="uid"
    nameField="label"
    parentIdField="parent"
    data='{[
      { uid: 1, icon:"folder", label: "Root Item 1", parent: null },
      { uid: 2, icon:"folder", label: "Child Item 1.1", parent: 1 },
      { uid: 3, icon: "code", label: "Child Item 1.2", parent: 1 },
      { uid: 4, icon: "code", label: "Grandchild Item 1.1.1", parent: 2 },
    ]}'>
  </Tree>
</App>
```

## Expanding and collapsing tree nodes

By default, when you click a tree node outside of its expand/collapse icon,  the specified item is selected. With the `expandOnItemClick` property (using the `true` value), you can change this behavior to expand or collapse the item when clicking its surface anywhere.

You can use the `defaultExpanded` property to specify what nodes you want to see expanded initially. You can set this property to a list of node IDs or a string. When you specify IDs, the component expands the hierarchy to reveal the specified nodes. When the value is a string, you can use these options:
- `none`: all nodes are collapsed (default)
- `first-level`: all first-level nodes are expanded
- `all`: all nodes are expanded

The following example demonstrates the use of `defaultExpanded` with tree node IDs:

```xmlui-pg display copy height="300px" /doc-root/ /proj-web/ /media-profile-pic/ name="Example: defaultExpanded with node IDs"
<App>
  <Tree
    testId="tree"
    dataFormat="flat"
    defaultExpanded="{['doc-root', 'proj-web', 'media-profile-pic']}"
    data='{[
      // Branch A: Documents
      { id: "doc-root", name: "[Documents]", parentId: null },
      { id: "doc-reports", name: "Reports", parentId: "doc-root" },
      { id: "doc-invoices", name: "Invoices", parentId: "doc-root" },
      { id: "doc-q1-report", name: "Q1 Report.pdf", parentId: "doc-reports" },
      { id: "doc-q2-report", name: "Q2 Report.pdf", parentId: "doc-reports" },
      { id: "doc-inv-001", name: "Invoice-001.pdf", parentId: "doc-invoices" },

      // Branch B: Projects
      { id: "proj-root", name: "Projects", parentId: null },
      { id: "proj-web", name: "[Web Apps]", parentId: "proj-root" },
      { id: "proj-mobile", name: "Mobile Apps", parentId: "proj-root" },
      { id: "proj-ecommerce", name: "E-commerce Site", parentId: "proj-web" },
      { id: "proj-dashboard", name: "Admin Dashboard", parentId: "proj-web" },
      { id: "proj-ios-app", name: "iOS Shopping App", parentId: "proj-mobile" },

      // Branch C: Media
      { id: "media-root", name: "Media", parentId: null },
      { id: "media-images", name: "Images", parentId: "media-root" },
      { id: "media-videos", name: "Videos", parentId: "media-root" },
      { id: "media-profile-pic", name: "[profile.jpg]", parentId: "media-images" },
      { id: "media-banner", name: "banner.png", parentId: "media-images" },
    ]}'>
  </Tree>
</App>
```

You have several options to style the icons representing the expanded or collapsed state:
- The icons used for the expanded and collapsed states can be changed with the `iconExpanded` and `iconCollapsed` properties, respectively.
- You can specify a different size with the `iconSize` property (using only numeric values considered as pixels)
- Using a rotate animation when changing the state with the `animateExpand` flag.
The following option demonstrates the last two options:

```xmlui-pg display copy {4-5} height="220px" name="Example: expand/collapse options"
<App>
  <Tree
    testId="tree"
    iconSize="24"
    animateExpand
    dataFormat="flat"
    defaultExpanded="all"
    data='{[
      { id: 1, name: "Root Item 1", parentId: null },
      { id: 2, name: "Child Item 1.1", parentId: 1 },
      { id: 3, name: "Child Item 1.2", parentId: 1 },
      { id: 4, name: "Grandchild Item 1.1.1", parentId: 2 },
    ]}'>
  </Tree>
</App>
```

## Selection

Each tree node is selectable by default, unless the node item's data does not have a `selectable` property (or the one specified in `selectedField`).
A selectable item can be selected by clicking the mouse or pressing the Enter or Space keys when it has focus.

You can set the `selectedValue` property to define the selected tree item, ot use the `selectNode` exposed method for imperative selection.

## Item templates

You can override the default template used to display a tree item with the `itemTemplate` property. The template definition can use the `$item` context variable to access the item's attributes for display. `$item` provides these properties:
- `id`: The unique node ID
- `name`: The name of the node
- `depth`: The depth level in the tree
- `isExpanded`: Indicates if the tree node is expanded
- `hasChildren`: Indicates if the tree node has children
- `children`: The children of the tree node
- `selectable`: Indicates if the node can be selected
- `parentId`: The ID of the node's parent
- `parentIds`: A list of parent IDs from the root node to the direct parent of the node
- `path`: An array with the node names following the path from the root node to the displayed node.
- `loadingState`: The current state of a dynamic node ("unloaded", "loading", or "loaded")

This example demonstrates these concepts:

```xmlui-pg display copy {20-30} height="400px" /$item.id/ /$item.name/ /$item.hasChildren/ name="Example: itemTemplate"
<App>
  <Tree
    testId="tree"
    id="tree"
    defaultExpanded="all"
    data='{[
        { id: "root", name: "My Files", parentId: null },
        { id: "doc-root", name: "Documents", parentId: "root" },
        { id: "doc-reports", name: "Reports", parentId: "doc-root" },
        { id: "doc-q1-report", name: "Q1 Report.pdf", parentId: "doc-reports" },
        { id: "doc-q2-report", name: "Q2 Report.pdf", parentId: "doc-reports" },
        { id: "proj-root", name: "Projects", parentId: "root" },
        { id: "proj-web", name: "Web Apps", parentId: "proj-root" },
        { id: "proj-ecommerce", name: "E-commerce Site", parentId: "proj-web" },
        { id: "proj-dashboard", name: "Admin Dashboard", parentId: "proj-web" },
        { id: "media-root", name: "Media", parentId: "root" },
        { id: "media-images", name: "Images", parentId: "media-root" },
        { id: "media-videos", name: "Videos", parentId: "media-root" },
      ]}'>
    <property name="itemTemplate">
      <HStack testId="{$item.id}" verticalAlignment="center" gap="$space-1">
        <Icon name="{$item.hasChildren ? 'folder' : 'code'}" />
        <Text>
          ({$item.id}):
        </Text>
        <Text variant="strong">
          {$item.name}
        </Text>
      </HStack>
    </property>
  </Tree>
</App>
```

## Dynamic tree nodes

When initializing the tree with its `data` property, you can set the `dynamic` property of the node to `true` (you can use a field name alias with the `dynamicField` property). When you extend a dynamic node, the tree fires the `loadChildren` event, and the nodes returned by the event handler will be the actual nodes.

By default, nodes are not dynamic.

While the child nodes are being queried, the tree node displays a spinner to indicate the loading state.

You can use the `markNodeUnloaded` exposed method to reset the state of an already loaded dynamic tree node. The next time the user expands the node, its content will be loaded again.

The following sample demonstrates this feature. Click the "Child Item 1.2" node to check how it loads its children. Click the Unload button to reload the items when the node is expanded the next time.

```xmlui-pg display copy {16-19} height="340px" /dynamic: true/ /onLoadChildren/ name="Example: dynamic nodes"
<App var.loadCount="{0}">
  <Tree
    testId="tree"
    defaultExpanded="all"
    id="tree"
    itemClickExpands
    data='{[
      { id: 1, name: "Root Item 1", parentId: null },
      { id: 2, name: "Child Item 1.1", parentId: 1 },
      { id: 3, name: "Child Item 1.2", parentId: 1, dynamic: true },
      { id: 4, name: "Child Item 1.3", parentId: 1 },
    ]}'
    onLoadChildren="(node) => {
      loadCount++;
      delay(1000); 
      return ([
        { id: 5, name: `Dynamic Item 1.2.1 (${loadCount})` },
        { id: 6, name: `Dynamic Item 2.2.2 (${loadCount})` },
      ])
    }"
    >
    <property name="itemTemplate">
      <HStack testId="{$item.id}" verticalAlignment="center" gap="$space-1">
        <Icon name="{$item.hasChildren 
          ? ($item.loadingState === 'loaded' ? 'folder' : 'folder-outline' ) 
          : 'code'}" 
        />
        <Text>{$item.name}</Text>
      </HStack>
    </property>
  </Tree>
  <Button onClick="tree.markNodeUnloaded(3)">Unload</Button>
</App>
```

%-DESC-END
```

--------------------------------------------------------------------------------
/xmlui/tests/parsers/scripting/statement-hooks.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, expect, it } from "vitest";

import { processStatementQueueAsync } from "../../../src/components-core/script-runner/process-statement-async";
import { processStatementQueue } from "../../../src/components-core/script-runner/process-statement-sync";
import { createEvalContext, parseStatements } from "./test-helpers";

describe("Statement hooks", () => {
  const stmtCases = [
    { src: "let x = 0;", exp: 1 },
    { src: "let x = 0; x++;", exp: 2 },
    { src: "let x = 0; while (x < 1) { x++ };", exp: 6 },
  ];

  stmtCases.forEach((c) => {
    it(`onStatementStarted (async) works ${c.src}`, async () => {
      // --- Arrange
      let counter = 0;
      const evalContext = createEvalContext({
        localContext: {},
        onStatementStarted: () => {
          counter++;
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(counter).equal(c.exp);
    });
  });

  stmtCases.forEach((c) => {
    it(`onStatementStarted (sync) works ${c.src}`, () => {
      // --- Arrange
      let counter = 0;
      const evalContext = createEvalContext({
        localContext: {},
        onStatementStarted: () => {
          counter++;
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(counter).equal(c.exp);
    });
  });

  stmtCases.forEach((c) => {
    it(`onStatementCompleted (async) works ${c.src}`, async () => {
      // --- Arrange
      let counter = 0;
      const evalContext = createEvalContext({
        localContext: {},
        onStatementCompleted: () => {
          counter++;
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(counter).equal(c.exp);
    });
  });

  stmtCases.forEach((c) => {
    it(`onStatementCompleted (sync) works ${c.src}`, () => {
      // --- Arrange
      let counter = 0;
      const evalContext = createEvalContext({
        localContext: {},
        onStatementCompleted: () => {
          counter++;
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(counter).equal(c.exp);
    });
  });

  stmtCases.forEach((c) => {
    it(`onStatementStarted&Completed (async) works ${c.src}`, async () => {
      // --- Arrange
      let counter = 0;
      const evalContext = createEvalContext({
        localContext: {},
        onStatementStarted: () => {
          counter++;
        },
        onStatementCompleted: () => {
          counter++;
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(counter).equal(c.exp + c.exp);
    });
  });

  stmtCases.forEach((c) => {
    it(`onStatementStarted&Completed (sync) works ${c.src}`, () => {
      // --- Arrange
      let counter = 0;
      const evalContext = createEvalContext({
        localContext: {},
        onStatementStarted: () => {
          counter++;
        },
        onStatementCompleted: () => {
          counter++;
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(counter).equal(c.exp + c.exp);
    });
  });

  const updateAsgnCases = [
    { src: "let x = 0; x = 3", exp: [] },
    { src: "x = 0;", exp: ["x"] },
    { src: "x.a = 0;", exp: ["x"] },
    { src: "x.a = []; x.a[3] = 12", exp: ["x", "x"] },
    { src: "x.a = []; x.a[3] = {}; x.a[3].f = 24", exp: ["x", "x", "x"] },
    { src: "x = 0; y = 0;", exp: ["x", "y"] },
  ];

  updateAsgnCases.forEach((c) => {
    it(`onWillUpdate (async) works ${c.src}`, async () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onWillUpdate: (_scope, index, type) => {
          if (type === "assignment") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  updateAsgnCases.forEach((c) => {
    it(`onWillUpdate (sync) works ${c.src}`, () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onWillUpdate: (_scope, index, type) => {
          if (type === "assignment") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  updateAsgnCases.forEach((c) => {
    it(`onDidUpdate (async) works ${c.src}`, async () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onDidUpdate: (_scope, index, type) => {
          if (type === "assignment") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  updateAsgnCases.forEach((c) => {
    it(`onDidUpdate (sync) works ${c.src}`, () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onDidUpdate: (_scope, index, type) => {
          if (type === "assignment") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  const prePostCases = [
    { src: "let x = 0; x++", exp: [] },
    { src: "x = 0; x++", exp: ["x"] },
    { src: "x.a = 0; ++x.a", exp: ["x"] },
    { src: "x.a = []; x.a[3] = 12; --x.a[3]", exp: ["x"] },
    { src: "x.a = []; x.a[3] = {}; x.a[3].f = 24; x.a[3].f--", exp: ["x"] },
    { src: "x = 0; x++; y = 0; --y;", exp: ["x", "y"] },
  ];

  prePostCases.forEach((c) => {
    it(`onWillUpdate (async) works ${c.src}`, async () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onWillUpdate: (_scope, index, type) => {
          if (type === "pre-post") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  prePostCases.forEach((c) => {
    it(`onWillUpdate (sync) works ${c.src}`, () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onWillUpdate: (_scope, index, type) => {
          if (type === "pre-post") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  prePostCases.forEach((c) => {
    it(`onDidUpdate (async) works ${c.src}`, async () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onDidUpdate: (_scope, index, type) => {
          if (type === "pre-post") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  prePostCases.forEach((c) => {
    it(`onDidUpdate (sync) works ${c.src}`, () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onDidUpdate: (_scope, index, type) => {
          if (type === "pre-post") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  const funcCallCases = [
    { src: "let x = () => {}; x()", exp: [] },
    { src: "x = () => {}; x()", exp: ["x"] },
    { src: "x.a = () => {}; x.a()", exp: ["x"] },
    { src: "x.a = []; x.a[3] = () => {}; x.a[3]()", exp: ["x"] },
    { src: "x.a = []; x.a[3] = {}; x.a[3].f = () => {}; x.a[3].f()", exp: ["x"] },
    { src: "x = () => {}; x(); y = () => {}; y();", exp: ["x", "y"] },
  ];

  funcCallCases.forEach((c) => {
    it(`onWillUpdate (async) works ${c.src}`, async () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onWillUpdate: (_scope, index, type) => {
          if (type === "function-call") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  funcCallCases.forEach((c) => {
    it(`onWillUpdate (sync) works ${c.src}`, () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onWillUpdate: (_scope, index, type) => {
          if (type === "function-call") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  funcCallCases.forEach((c) => {
    it(`onDidUpdate (async) works ${c.src}`, async () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onDidUpdate: (_scope, index, type) => {
          if (type === "function-call") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      await processStatementQueueAsync(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });

  funcCallCases.forEach((c) => {
    it(`onDidUpdate (sync) works ${c.src}`, () => {
      // --- Arrange
      let updated: string[] = [];
      const evalContext = createEvalContext({
        localContext: {
          x: {},
          y: {},
        },
        onDidUpdate: (_scope, index, type) => {
          if (type === "function-call") {
            updated.push(index as string);
          }
        },
      });
      const statements = parseStatements(c.src);

      // --- Act
      processStatementQueue(statements, evalContext);

      // --- Assert
      expect(updated).toStrictEqual(c.exp);
    });
  });
});

```

--------------------------------------------------------------------------------
/xmlui/src/components-core/action/APICall.tsx:
--------------------------------------------------------------------------------

```typescript
import toast from "react-hot-toast";
import type { QueryClient, QueryKey } from "@tanstack/react-query";
import { createDraft, finishDraft } from "immer";

import type { AppContextObject } from "../../abstractions/AppContextDefs";
import type { AsyncFunction } from "../../abstractions/FunctionDefs";
import type { ActionExecutionContext, LookupAsyncFnInner } from "../../abstractions/ActionDefs";
import { invalidateQueries } from "../utils/actionUtils";
import { extractParam, shouldKeep } from "../utils/extractParam";
import { randomUUID } from "../utils/misc";
import type { ApiActionOptions, ApiOperationDef } from "../RestApiProxy";
import RestApiProxy from "../RestApiProxy";
import { createAction } from "./actions";

function findQueryKeysToUpdate(updates: string | string[], queryClient: QueryClient) {
  const queryKeysToUpdate: Array<QueryKey> = [];
  if (updates) {
    let updatesArray: Array<string>;
    if (Array.isArray(updates)) {
      updatesArray = updates;
    } else {
      updatesArray = [updates];
    }
    updatesArray.forEach((queryUrl) => {
      queryClient
        .getQueryCache()
        .getAll()
        .forEach((query) => {
          if (query.queryKey[0] === queryUrl) {
            queryKeysToUpdate.push(query.queryKey);
          }
        });
    });
  }
  return queryKeysToUpdate;
}

function prepareOptimisticValue(value: any, clientTxId: string) {
  return {
    ...value,
    id: value.id || clientTxId,
    _optimisticValue: true,
    _initiatorClientTxId: clientTxId,
  };
}

async function prepareOptimisticValuesForQueries(
  queryKeys: Array<QueryKey>,
  queryClient: QueryClient,
  clientTxId: string,
  stateContext: any,
  resolvedOptimisticValue?: any,
  optimisticValueGetter?: AsyncFunction,
) {
  const ret: Map<QueryKey, any> = new Map();

  await Promise.all(
    queryKeys.map(async (queryKey) => {
      if (resolvedOptimisticValue) {
        ret.set(queryKey, prepareOptimisticValue(resolvedOptimisticValue, clientTxId));
        return;
      }
      if (!optimisticValueGetter) {
        return;
      }
      const currentData = queryClient.getQueryData(queryKey) as any;
      const flatData = currentData?.pages
        ? currentData.pages.flatMap((page: any) => page)
        : currentData;
      const optimisticValue = await optimisticValueGetter(flatData, stateContext["$param"]);
      if (optimisticValue) {
        ret.set(queryKey, prepareOptimisticValue(optimisticValue, clientTxId));
      }
    }),
  );

  return ret;
}

async function doOptimisticUpdate(
  optimisticValuesByQueryKeys: Map<QueryKey, any>,
  queryClient: QueryClient,
) {
  if (!optimisticValuesByQueryKeys.size) {
    return;
  }
  for (const entry of optimisticValuesByQueryKeys.entries()) {
    const [key, optimisticValue] = entry;
    await queryClient.cancelQueries({ queryKey: key });
    const oldData = queryClient.getQueryData(key) as any;

    const draft = createDraft(oldData as any);
    if (draft.pages) {
      let updated = false;
      draft.pages.forEach((page: any) => {
        page.forEach((item: any) => {
          if (item.id === optimisticValue.id) {
            Object.assign(item, optimisticValue);
            updated = true;
          }
        });
      });
      if (!updated) {
        draft.pages[draft.pages.length - 1].push(optimisticValue);
      }
    } else {
      let updated = false;
      draft.forEach((item: any) => {
        if (item.id === optimisticValue.id) {
          Object.assign(item, optimisticValue);
          updated = true;
        }
      });
      if (!updated) {
        draft.push(optimisticValue);
      }
    }
    const newData = finishDraft(draft);
    queryClient.setQueryData(key, newData);
    // console.log("optimistic added", { finalOptimisticValue, newData });
  }
}

function updateQueriesWithResult(
  queryKeysToUpdate: Array<QueryKey>,
  optimisticValuesByQueryKeys: Map<QueryKey, any>,
  clientTxId: string,
  queryClient: QueryClient,
  result: any,
) {
  if (!queryKeysToUpdate.length) {
    return;
  }
  queryKeysToUpdate.forEach((key) => {
    const oldData = queryClient.getQueryData(key) as any;
    const draft = createDraft(oldData as any);
    const optimisticValue = optimisticValuesByQueryKeys.get(key);
    if (draft.pages) {
      //pageable loader
      if (optimisticValue) {
        draft.pages[draft.pages.length - 1] = draft.pages[draft.pages.length - 1].map(
          (item: any) =>
            item.id === optimisticValue.id && item._initiatorClientTxId === clientTxId
              ? result || {
              ...item,
              _optimisticValue: undefined,
              _initiatorClientTxId: undefined,
            }
              : item,
        );
      } else {
        let updated = false;
        draft.pages.forEach((page: any) => {
          page?.forEach((item: any) => {
            if (item.id === result?.id) {
              Object.assign(item, result);
              updated = true;
            }
          });
        });
        if (!updated && result) {
          draft.pages[draft.pages.length - 1].push(result);
        }
      }
    } else {
      if (optimisticValue) {
        draft.forEach((item: any, index: number) => {
          if (item.id === optimisticValue.id && item._initiatorClientTxId === clientTxId) {
            draft[index] = result || {
              ...item,
              _optimisticValue: undefined,
              _initiatorClientTxId: undefined,
            };
          }
        });
      } else {
        let updated = false;
        draft.forEach((item: any, index: number) => {
          if (item.id === result.id) {
            draft[index] = result || {
              ...item,
              _optimisticValue: undefined,
              _initiatorClientTxId: undefined,
            };
            updated = true;
          }
        });
        if (!updated && result) {
          draft.push(result);
        }
      }
    }
    const newData = finishDraft(draft);
    queryClient.setQueryData(key, newData);
  });
}

async function updateQueriesWithOptimisticValue({
                                                  stateContext,
                                                  updates,
                                                  appContext,
                                                  queryClient,
                                                  clientTxId,
                                                  optimisticValue,
                                                  lookupAction,
                                                  getOptimisticValue,
                                                  uid,
                                                }: {
  stateContext: any;
  updates: string | string[] | undefined;
  appContext: AppContextObject;
  queryClient: QueryClient;
  clientTxId: string;
  optimisticValue: any;
  lookupAction: LookupAsyncFnInner;
  getOptimisticValue?: string;
  uid: symbol;
}) {
  const queryKeysToUpdate = findQueryKeysToUpdate(
    extractParam(stateContext, updates, appContext),
    queryClient,
  );
  const optimisticValuesByQueryKeys = await prepareOptimisticValuesForQueries(
    queryKeysToUpdate,
    queryClient,
    clientTxId,
    stateContext,
    extractParam(stateContext, optimisticValue, appContext),
    lookupAction(getOptimisticValue, uid),
  );

  await doOptimisticUpdate(optimisticValuesByQueryKeys, queryClient);
  return { queryKeysToUpdate, optimisticValuesByQueryKeys };
}

type APICall = {
  invalidates?: string | string[];
  updates?: string | string[];
  confirmTitle?: string;
  confirmMessage?: string;
  confirmButtonLabel?: string;
  params?: any;
  payloadType?: string;
  optimisticValue?: any;
  getOptimisticValue?: string;
  inProgressNotificationMessage?: string;
  completedNotificationMessage?: string;
  errorNotificationMessage?: string;

  uid?: string | symbol;
  when?: string;

  onBeforeRequest?: string;
  onSuccess?: string;
  onProgress?: string;
  onError?: string;
} & ApiOperationDef;

export async function callApi(
  { state, appContext, lookupAction, getCurrentState, apiInstance }: ActionExecutionContext,
  {
    confirmTitle,
    confirmMessage,
    confirmButtonLabel,
    params = {},
    onBeforeRequest,
    onSuccess,
    onError,
    invalidates,
    updates,
    optimisticValue,
    payloadType,
    when,
    getOptimisticValue,
    inProgressNotificationMessage,
    completedNotificationMessage,
    errorNotificationMessage,
    uid: actionUid,
    onProgress,

    //operation
    headers,
    url,
    queryParams,
    rawBody,
    method,
    body,
  }: APICall,
  { resolveBindingExpressions }: ApiActionOptions = {},
) {
  const uid = typeof actionUid === "symbol" ? actionUid : Symbol(actionUid);
  const stateContext = { ...state, ...params };
  if (!shouldKeep(when, stateContext, appContext)) {
    return;
  }
  if (confirmTitle || confirmMessage || confirmButtonLabel) {
    const title = extractParam(stateContext, confirmTitle, appContext);
    const message = extractParam(stateContext, confirmMessage, appContext);
    const buttonLabel = extractParam(stateContext, confirmButtonLabel, appContext);
    const dialogCheck = await appContext.confirm(
      title ?? "Confirm Operation",
      message ?? "Are you sure you want to perform this operation?",
      buttonLabel ?? "Yes",
    );
    if (!dialogCheck) return;
  }
  const resolvedInvalidates = extractParam(stateContext, invalidates, appContext);

  const clientTxId = randomUUID();
  const beforeRequestFn = lookupAction(onBeforeRequest, uid);
  const beforeRequestResult = await beforeRequestFn?.();
  if (typeof beforeRequestResult === "boolean" && beforeRequestResult === false) {
    return;
  }

  const { queryKeysToUpdate, optimisticValuesByQueryKeys } = await updateQueriesWithOptimisticValue(
    {
      stateContext,
      updates,
      appContext,
      queryClient: appContext.queryClient!,
      clientTxId,
      optimisticValue,
      lookupAction,
      getOptimisticValue,
      uid,
    },
  );

  const inProgressMessage = extractParam(stateContext, inProgressNotificationMessage, appContext);

  let loadingToastId;
  if (inProgressMessage) {
    loadingToastId = toast.loading(inProgressMessage);
  }
  try {
    const operation: ApiOperationDef = {
      headers,
      url,
      queryParams,
      rawBody,
      method,
      body,
      payloadType,
    };
    const _onProgress = lookupAction(onProgress, uid, {
      eventName: "progress",
    });
    const result = await new RestApiProxy(appContext, apiInstance).execute({
      operation,
      params: stateContext,
      transactionId: clientTxId,
      resolveBindingExpressions,
      onProgress: _onProgress,
    });

    const onSuccessFn = lookupAction(onSuccess, uid, {
      eventName: "success",
      context: getCurrentState()
    });
    await onSuccessFn?.(result, stateContext["$param"]);

    updateQueriesWithResult(
      queryKeysToUpdate,
      optimisticValuesByQueryKeys,
      clientTxId,
      appContext.queryClient!,
      result,
    );

    if (resolvedInvalidates || !updates) {
      await invalidateQueries(resolvedInvalidates, appContext, state);
    }
    const completedMessage = extractParam(
      { ...stateContext, $result: result },
      completedNotificationMessage,
      appContext,
    );
    if (completedMessage) {
      toast.success(completedMessage, {
        id: loadingToastId,
      });
    } else if (loadingToastId) {
      toast.dismiss(loadingToastId);
    }
    return result;
  } catch (e) {
    if (optimisticValuesByQueryKeys.size) {
      await appContext.queryClient!.invalidateQueries();
    }
    const onErrorFn = lookupAction(onError, uid, {
      eventName: "error",
    });
    const result = await onErrorFn?.(e, stateContext["$param"]);
    const errorMessage = extractParam(
      { ...stateContext, $error: e },
      errorNotificationMessage,
      appContext,
    );
    if (errorMessage) {
      toast.error(errorMessage, {
        id: loadingToastId,
      });
    } else {
      if (loadingToastId) {
        toast.dismiss(loadingToastId);
      }
      if (result !== false) {
        //stop the error propagation, if the error handler returns false
        throw e;
      }
    }
  }
}

export const apiAction = createAction("callApi", callApi);

```
Page 44/141FirstPrevNextLast