#
tokens: 47344/50000 3/1630 files (page 99/181)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 99 of 181. Use http://codebase.md/xmlui-org/xmlui/xmlui/mockApiDef.js?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   ├── config.json
│   ├── curly-llamas-try.md
│   ├── loose-trains-sit.md
│   ├── moody-pans-poke.md
│   ├── rare-cooks-write.md
│   ├── silver-llamas-cough.md
│   └── true-jeans-agree.md
├── .eslintrc.cjs
├── .github
│   ├── build-checklist.png
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── 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.mp4
│   │   │   │   ├── blog-page-component.png
│   │   │   │   ├── blog-scrabble.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
│   │           ├── HelloMd.md
│   │           ├── HeroSection.md
│   │           └── ScrollToTop.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── feed.rss
│   │   ├── mockServiceWorker.js
│   │   ├── pages
│   │   │   ├── _meta.json
│   │   │   ├── app-structure.md
│   │   │   ├── build-editor-component.md
│   │   │   ├── build-hello-world-component.md
│   │   │   ├── components-intro.md
│   │   │   ├── context-variables.md
│   │   │   ├── forms.md
│   │   │   ├── globals.md
│   │   │   ├── glossary.md
│   │   │   ├── helper-tags.md
│   │   │   ├── hosted-deployment.md
│   │   │   ├── howto
│   │   │   │   ├── assign-a-complex-json-literal-to-a-component-variable.md
│   │   │   │   ├── chain-a-refetch.md
│   │   │   │   ├── control-cache-invalidation.md
│   │   │   │   ├── 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/src/components/Select/SelectNative.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import {
  2 |   forwardRef,
  3 |   useCallback,
  4 |   useEffect,
  5 |   useMemo,
  6 |   useRef,
  7 |   useState,
  8 |   type CSSProperties,
  9 |   type ReactNode,
 10 | } from "react";
 11 | import { Popover, PopoverContent, PopoverTrigger, Portal } from "@radix-ui/react-popover";
 12 | import classnames from "classnames";
 13 | import styles from "./Select.module.scss";
 14 | import { composeRefs } from "@radix-ui/react-compose-refs";
 15 | 
 16 | import type { RegisterComponentApiFn, UpdateStateFn } from "../../abstractions/RendererDefs";
 17 | import { noop } from "../../components-core/constants";
 18 | import { useTheme } from "../../components-core/theming/ThemeContext";
 19 | import { useEvent } from "../../components-core/utils/misc";
 20 | import type { Option, ValidationStatus } from "../abstractions";
 21 | import Icon from "../Icon/IconNative";
 22 | import { SelectContext, useSelect } from "./SelectContext";
 23 | import OptionTypeProvider from "../Option/OptionTypeProvider";
 24 | import { OptionContext, useOption } from "./OptionContext";
 25 | import { HiddenOption } from "./HiddenOption";
 26 | 
 27 | const PART_LIST_WRAPPER = "listWrapper";
 28 | 
 29 | export const defaultProps = {
 30 |   enabled: true,
 31 |   placeholder: "",
 32 |   autoFocus: false,
 33 |   searchable: false,
 34 |   multiSelect: false,
 35 |   required: false,
 36 |   inProgress: false,
 37 |   inProgressNotificationMessage: "",
 38 |   readOnly: false,
 39 |   validationStatus: "none" as ValidationStatus,
 40 |   labelBreak: false,
 41 | };
 42 | 
 43 | export type SingleValueType = string | number;
 44 | export type ValueType = SingleValueType | SingleValueType[];
 45 | 
 46 | // Core Select component props
 47 | interface SelectProps {
 48 |   // Basic props
 49 |   id?: string;
 50 |   initialValue?: ValueType;
 51 |   value?: ValueType;
 52 |   enabled?: boolean;
 53 |   placeholder?: string;
 54 |   autoFocus?: boolean;
 55 |   readOnly?: boolean;
 56 |   required?: boolean;
 57 | 
 58 |   // Styling
 59 |   style?: CSSProperties;
 60 |   className?: string;
 61 |   dropdownHeight?: CSSProperties["height"];
 62 | 
 63 |   // Validation
 64 |   validationStatus?: ValidationStatus;
 65 | 
 66 |   // Event handlers
 67 |   onDidChange?: (newValue: ValueType) => void;
 68 |   onFocus?: () => void;
 69 |   onBlur?: () => void;
 70 | 
 71 |   // Multi-select and search
 72 |   searchable?: boolean;
 73 |   multiSelect?: boolean;
 74 | 
 75 |   // Templates and renderers (legacy - kept for compatibility)
 76 |   valueRenderer?: (item: Option, removeItem: () => void) => ReactNode;
 77 |   emptyListTemplate?: ReactNode;
 78 |   optionRenderer?: (item: Option, value: any, inTrigger: boolean) => ReactNode;
 79 | 
 80 |   // Progress state
 81 |   inProgress?: boolean;
 82 |   inProgressNotificationMessage?: string;
 83 | 
 84 |   // Internal
 85 |   updateState?: UpdateStateFn;
 86 |   registerComponentApi?: RegisterComponentApiFn;
 87 |   children?: ReactNode;
 88 | }
 89 | 
 90 | // Common trigger value display props
 91 | interface SelectTriggerValueProps {
 92 |   value: ValueType;
 93 |   placeholder: string;
 94 |   readOnly: boolean;
 95 |   multiSelect: boolean;
 96 |   options: Set<Option>;
 97 |   valueRenderer?: (item: Option, removeItem: () => void) => ReactNode;
 98 |   toggleOption: (value: SingleValueType) => void;
 99 | }
100 | 
101 | // Common trigger value display component
102 | const SelectTriggerValue = ({
103 |   value,
104 |   placeholder,
105 |   readOnly,
106 |   multiSelect,
107 |   options,
108 |   valueRenderer,
109 |   toggleOption,
110 | }: SelectTriggerValueProps) => {
111 |   if (multiSelect) {
112 |     if (Array.isArray(value) && value.length > 0) {
113 |       return (
114 |         <div className={styles.badgeListContainer}>
115 |           <div className={styles.badgeList}>
116 |             {value.map((v) =>
117 |               valueRenderer ? (
118 |                 valueRenderer(
119 |                   Array.from(options).find((o) => o.value === `${v}`),
120 |                   () => {
121 |                     toggleOption(v);
122 |                   },
123 |                 )
124 |               ) : (
125 |                 <span key={v} className={styles.badge}>
126 |                   {Array.from(options).find((o) => o.value === `${v}`)?.label}
127 |                   <Icon
128 |                     name="close"
129 |                     size="sm"
130 |                     onClick={(event) => {
131 |                       event.stopPropagation();
132 |                       toggleOption(v);
133 |                     }}
134 |                   />
135 |                 </span>
136 |               ),
137 |             )}
138 |           </div>
139 |         </div>
140 |       );
141 |     }
142 |     return (
143 |       <span placeholder={placeholder} className={styles.placeholder}>
144 |         {placeholder}
145 |       </span>
146 |     );
147 |   }
148 | 
149 |   // Single select
150 |   if (value !== undefined && value !== null && value !== "") {
151 |     const selectedOption = Array.from(options).find((o) => o.value === value);
152 |     return <div className={styles.selectValue}>{selectedOption?.label}</div>;
153 |   }
154 | 
155 |   return (
156 |     <div aria-placeholder={placeholder} className={styles.placeholder}>
157 |       {readOnly ? "" : placeholder || ""}
158 |     </div>
159 |   );
160 | };
161 | 
162 | // Common trigger actions (clear button + chevron)
163 | interface SelectTriggerActionsProps {
164 |   value: ValueType;
165 |   multiSelect: boolean;
166 |   enabled: boolean;
167 |   readOnly: boolean;
168 |   clearValue: () => void;
169 |   showChevron?: boolean;
170 | }
171 | 
172 | const SelectTriggerActions = ({
173 |   value,
174 |   multiSelect,
175 |   enabled,
176 |   readOnly,
177 |   clearValue,
178 |   showChevron = true,
179 | }: SelectTriggerActionsProps) => {
180 |   const hasValue = multiSelect
181 |     ? Array.isArray(value) && value.length > 0
182 |     : value !== undefined && value !== null && value !== "";
183 | 
184 |   return (
185 |     <div className={styles.actions}>
186 |       {hasValue && enabled && !readOnly && (
187 |         <span
188 |           className={styles.action}
189 |           onClick={(event) => {
190 |             event.stopPropagation();
191 |             clearValue();
192 |           }}
193 |         >
194 |           <Icon name="close" />
195 |         </span>
196 |       )}
197 |       {showChevron && (
198 |         <span className={styles.action}>
199 |           <Icon name="chevrondown" />
200 |         </span>
201 |       )}
202 |     </div>
203 |   );
204 | };
205 | 
206 | export const Select = forwardRef<HTMLDivElement, SelectProps>(function Select(
207 |   {
208 |     // Basic props
209 |     id,
210 |     initialValue,
211 |     value,
212 |     enabled = defaultProps.enabled,
213 |     placeholder = defaultProps.placeholder,
214 |     autoFocus = defaultProps.autoFocus,
215 |     readOnly = false,
216 |     required = defaultProps.required,
217 | 
218 |     // Styling
219 |     style,
220 |     className,
221 |     dropdownHeight,
222 | 
223 |     // Validation
224 |     validationStatus = defaultProps.validationStatus,
225 | 
226 |     // Event handlers
227 |     onDidChange = noop,
228 |     onFocus = noop,
229 |     onBlur = noop,
230 | 
231 |     // Multi-select and search
232 |     searchable = defaultProps.searchable,
233 |     multiSelect = defaultProps.multiSelect,
234 | 
235 |     emptyListTemplate,
236 |     valueRenderer,
237 |     optionRenderer,
238 | 
239 |     // Progress state
240 |     inProgress = defaultProps.inProgress,
241 |     inProgressNotificationMessage = defaultProps.inProgressNotificationMessage,
242 | 
243 |     // Internal
244 |     updateState = noop,
245 |     registerComponentApi,
246 |     children,
247 | 
248 |     ...rest
249 |   },
250 |   forwardedRef,
251 | ) {
252 |   const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null);
253 |   const [open, setOpen] = useState(false);
254 |   const [width, setWidth] = useState(0);
255 |   const observer = useRef<ResizeObserver>();
256 |   const { root } = useTheme();
257 |   const [options, setOptions] = useState(new Set<Option>());
258 |   const [searchTerm, setSearchTerm] = useState("");
259 |   const [selectedIndex, setSelectedIndex] = useState(-1);
260 | 
261 |   // Filter options based on search term
262 |   const filteredOptions = useMemo(() => {
263 |     if (!searchTerm || searchTerm.trim() === "") {
264 |       return Array.from(options);
265 |     }
266 | 
267 |     const searchLower = searchTerm.toLowerCase();
268 |     return Array.from(options).filter((option) => {
269 |       const extendedValue =
270 |         option.value + " " + option.label + " " + (option.keywords || []).join(" ");
271 |       return extendedValue.toLowerCase().includes(searchLower);
272 |     });
273 |   }, [options, searchTerm]);
274 | 
275 |   // Reset selected index when options change or dropdown closes
276 |   useEffect(() => {
277 |     if (!open) {
278 |       setSelectedIndex(-1);
279 |       setSearchTerm("");
280 |     }
281 |   }, [open]);
282 | 
283 |   // Set initial state based on the initialValue prop
284 |   useEffect(() => {
285 |     if (initialValue !== undefined) {
286 |       updateState({ value: initialValue }, { initial: true });
287 |     }
288 |   }, [initialValue, updateState]);
289 | 
290 |   // Observe the size of the reference element
291 |   useEffect(() => {
292 |     const current = referenceElement;
293 |     observer.current?.disconnect();
294 | 
295 |     if (current) {
296 |       observer.current = new ResizeObserver(() => setWidth(current.clientWidth));
297 |       observer.current.observe(current);
298 |     }
299 | 
300 |     return () => {
301 |       observer.current?.disconnect();
302 |     };
303 |   }, [referenceElement]);
304 | 
305 |   // Handle option selection
306 |   const toggleOption = useCallback(
307 |     (selectedValue: SingleValueType) => {
308 |       const newSelectedValue = multiSelect
309 |         ? Array.isArray(value)
310 |           ? value.map((v) => String(v)).includes(String(selectedValue))
311 |             ? value.filter((v) => String(v) !== String(selectedValue))
312 |             : [...value, selectedValue]
313 |           : [selectedValue]
314 |         : String(selectedValue) === String(value)
315 |           ? null
316 |           : selectedValue;
317 |       updateState({ value: newSelectedValue });
318 |       onDidChange(newSelectedValue);
319 |       if (!multiSelect) {
320 |         setOpen(false);
321 |       }
322 |     },
323 |     [multiSelect, value, updateState, onDidChange],
324 |   );
325 | 
326 |   // Clear selected value
327 |   const clearValue = useCallback(() => {
328 |     const newValue = multiSelect ? [] : "";
329 |     updateState({ value: newValue });
330 |     onDidChange(newValue);
331 |   }, [multiSelect, updateState, onDidChange]);
332 | 
333 |   // Helper functions to find next/previous enabled option
334 |   const findNextEnabledIndex = useCallback(
335 |     (currentIndex: number) => {
336 |       if (filteredOptions.length === 0) return -1;
337 | 
338 |       for (let i = currentIndex + 1; i < filteredOptions.length; i++) {
339 |         const item = filteredOptions[i];
340 |         if (item && item.enabled !== false) {
341 |           return i;
342 |         }
343 |       }
344 |       // Wrap around to beginning
345 |       for (let i = 0; i <= currentIndex; i++) {
346 |         const item = filteredOptions[i];
347 |         if (item && item.enabled !== false) {
348 |           return i;
349 |         }
350 |       }
351 |       return -1;
352 |     },
353 |     [filteredOptions],
354 |   );
355 | 
356 |   const findPreviousEnabledIndex = useCallback(
357 |     (currentIndex: number) => {
358 |       if (filteredOptions.length === 0) return -1;
359 | 
360 |       for (let i = currentIndex - 1; i >= 0; i--) {
361 |         const item = filteredOptions[i];
362 |         if (item && item.enabled !== false) {
363 |           return i;
364 |         }
365 |       }
366 |       // Wrap around to end
367 |       for (let i = filteredOptions.length - 1; i >= currentIndex; i--) {
368 |         const item = filteredOptions[i];
369 |         if (item && item.enabled !== false) {
370 |           return i;
371 |         }
372 |       }
373 |       return -1;
374 |     },
375 |     [filteredOptions],
376 |   );
377 | 
378 |   // Keyboard navigation
379 |   const handleKeyDown = useCallback(
380 |     (event: React.KeyboardEvent) => {
381 |       if (!open) return;
382 | 
383 |       switch (event.key) {
384 |         case "ArrowDown":
385 |           event.preventDefault();
386 |           setSelectedIndex((prev) => {
387 |             const nextIndex = findNextEnabledIndex(prev);
388 |             return nextIndex !== -1 ? nextIndex : prev;
389 |           });
390 |           break;
391 |         case "ArrowUp":
392 |           event.preventDefault();
393 |           setSelectedIndex((prev) => {
394 |             const prevIndex = findPreviousEnabledIndex(prev);
395 |             return prevIndex !== -1 ? prevIndex : prev;
396 |           });
397 |           break;
398 |         case "Enter":
399 |           event.preventDefault();
400 |           if (selectedIndex >= 0 && selectedIndex < filteredOptions.length) {
401 |             const selectedItem = filteredOptions[selectedIndex];
402 |             if (selectedItem && selectedItem.enabled !== false) {
403 |               toggleOption(selectedItem.value);
404 |               // Close dropdown after selecting in single-select mode
405 |               if (!multiSelect) {
406 |                 setOpen(false);
407 |               }
408 |             }
409 |           }
410 |           break;
411 |         case "Escape":
412 |           event.preventDefault();
413 |           setOpen(false);
414 |           break;
415 |       }
416 |     },
417 |     [
418 |       open,
419 |       selectedIndex,
420 |       filteredOptions,
421 |       toggleOption,
422 |       multiSelect,
423 |       findNextEnabledIndex,
424 |       findPreviousEnabledIndex,
425 |     ],
426 |   );
427 | 
428 |   // Register component API for external interactions
429 |   const focus = useCallback(() => {
430 |     referenceElement?.focus();
431 |   }, [referenceElement]);
432 | 
433 |   const setValue = useEvent((newValue: string) => {
434 |     toggleOption(newValue);
435 |   });
436 | 
437 |   const reset = useEvent(() => {
438 |     if (initialValue !== undefined) {
439 |       updateState({ value: initialValue });
440 |       onDidChange(initialValue);
441 |     } else {
442 |       clearValue();
443 |     }
444 |   });
445 | 
446 |   useEffect(() => {
447 |     registerComponentApi?.({
448 |       focus,
449 |       setValue,
450 |       reset,
451 |     });
452 |   }, [focus, registerComponentApi, setValue, reset]);
453 | 
454 |   // Render the "empty list" message
455 |   const emptyListNode = useMemo(
456 |     () =>
457 |       emptyListTemplate ?? (
458 |         <div className={styles.selectEmpty}>
459 |           <Icon name="noresult" />
460 |           <span>List is empty</span>
461 |         </div>
462 |       ),
463 |     [emptyListTemplate],
464 |   );
465 | 
466 |   const onOptionAdd = useCallback((option: Option) => {
467 |     setOptions((prev) => {
468 |       // Check if option with same value already exists
469 |       const exists = Array.from(prev).some((opt) => opt.value === option.value);
470 |       if (exists) {
471 |         return prev;
472 |       }
473 |       const newSet = new Set(prev);
474 |       newSet.add(option);
475 |       return newSet;
476 |     });
477 |   }, []);
478 | 
479 |   const onOptionRemove = useCallback((option: Option) => {
480 |     setOptions((prev) => {
481 |       const optionsSet = new Set(prev);
482 |       optionsSet.delete(option);
483 |       return optionsSet;
484 |     });
485 |   }, []);
486 | 
487 |   const optionContextValue = useMemo(
488 |     () => ({
489 |       onOptionAdd,
490 |       onOptionRemove,
491 |     }),
492 |     [onOptionAdd, onOptionRemove],
493 |   );
494 | 
495 |   const selectContextValue = useMemo(
496 |     () => ({
497 |       multiSelect,
498 |       readOnly,
499 |       value,
500 |       onChange: toggleOption,
501 |       setOpen,
502 |       setSelectedIndex,
503 |       options,
504 |       highlightedValue:
505 |         selectedIndex >= 0 &&
506 |         selectedIndex < filteredOptions.length &&
507 |         filteredOptions[selectedIndex]
508 |           ? filteredOptions[selectedIndex].value
509 |           : undefined,
510 |       optionRenderer,
511 |     }),
512 |     [
513 |       multiSelect,
514 |       readOnly,
515 |       value,
516 |       toggleOption,
517 |       options,
518 |       selectedIndex,
519 |       filteredOptions,
520 |       optionRenderer,
521 |     ],
522 |   );
523 | 
524 |   return (
525 |     <SelectContext.Provider value={selectContextValue}>
526 |       <OptionContext.Provider value={optionContextValue}>
527 |         <OptionTypeProvider Component={VisibleSelectOption}>
528 |           <Popover
529 |             open={open}
530 |             onOpenChange={(isOpen) => {
531 |               if (!enabled) return;
532 |               setOpen(isOpen);
533 |               // Reset highlighted option when dropdown closes
534 |               setSelectedIndex(-1);
535 |             }}
536 |             modal={false}
537 |           >
538 |             <PopoverTrigger
539 |               {...rest}
540 |               ref={composeRefs(setReferenceElement, forwardedRef)}
541 |               id={id}
542 |               aria-haspopup="listbox"
543 |               style={style}
544 |               onFocus={onFocus}
545 |               onBlur={onBlur}
546 |               disabled={!enabled}
547 |               aria-expanded={open}
548 |               data-part-id={PART_LIST_WRAPPER}
549 |               className={classnames(className, styles.selectTrigger, styles[validationStatus], {
550 |                 [styles.disabled]: !enabled,
551 |                 [styles.multi]: multiSelect,
552 |               })}
553 |               role="combobox"
554 |               onClick={(event) => {
555 |                 if (!enabled) return;
556 |                 event.stopPropagation();
557 |                 setOpen((prev) => !prev);
558 |               }}
559 |               onKeyDown={(event) => {
560 |                 if (!enabled || readOnly) return;
561 | 
562 |                 // Handle opening dropdown with keyboard
563 |                 if (
564 |                   !open &&
565 |                   (event.key === "ArrowDown" ||
566 |                     event.key === "ArrowUp" ||
567 |                     event.key === " " ||
568 |                     event.key === "Enter")
569 |                 ) {
570 |                   event.preventDefault();
571 |                   setOpen(true);
572 |                   // Set initial selectedIndex to first enabled option if options exist
573 |                   if (filteredOptions.length > 0) {
574 |                     const firstEnabledIndex = findNextEnabledIndex(-1);
575 |                     setSelectedIndex(firstEnabledIndex !== -1 ? firstEnabledIndex : 0);
576 |                   }
577 |                   return;
578 |                 }
579 | 
580 |                 // Handle keyboard navigation when dropdown is open
581 |                 if (open) {
582 |                   handleKeyDown(event);
583 |                 }
584 |               }}
585 |               autoFocus={autoFocus}
586 |             >
587 |               <SelectTriggerValue
588 |                 value={value}
589 |                 placeholder={placeholder}
590 |                 readOnly={readOnly}
591 |                 multiSelect={multiSelect}
592 |                 options={options}
593 |                 valueRenderer={valueRenderer}
594 |                 toggleOption={toggleOption}
595 |               />
596 |               <SelectTriggerActions
597 |                 value={value}
598 |                 multiSelect={multiSelect}
599 |                 enabled={enabled}
600 |                 readOnly={readOnly}
601 |                 clearValue={clearValue}
602 |               />
603 |             </PopoverTrigger>
604 |             {open && (
605 |               <Portal container={root}>
606 |                 <PopoverContent
607 |                   style={{ minWidth: width, height: dropdownHeight }}
608 |                   className={styles.selectContent}
609 |                   onKeyDown={handleKeyDown}
610 |                 >
611 |                   <div className={styles.command}>
612 |                     {searchable ? (
613 |                       <div className={styles.commandInputContainer}>
614 |                         <Icon name="search" />
615 |                         <input
616 |                           role="searchbox"
617 |                           className={classnames(styles.commandInput)}
618 |                           placeholder="Search..."
619 |                           value={searchTerm}
620 |                           onChange={(e) => setSearchTerm(e.target.value)}
621 |                         />
622 |                       </div>
623 |                     ) : (
624 |                       <button aria-hidden="true" className={styles.srOnly} />
625 |                     )}
626 |                     <div role="listbox" className={styles.commandList}>
627 |                       {inProgress ? (
628 |                         <div className={styles.loading}>{inProgressNotificationMessage}</div>
629 |                       ) : searchable && searchTerm ? (
630 |                         // When searching, show only filtered options
631 |                         filteredOptions.length === 0 ? (
632 |                           <div>{emptyListNode}</div>
633 |                         ) : (
634 |                           filteredOptions.map(({ value, label, enabled, keywords }, index) => (
635 |                             <SelectOptionItem
636 |                               key={value}
637 |                               readOnly={readOnly}
638 |                               value={value}
639 |                               label={label}
640 |                               enabled={enabled}
641 |                               keywords={keywords}
642 |                               isHighlighted={selectedIndex === index}
643 |                               itemIndex={index}
644 |                             />
645 |                           ))
646 |                         )
647 |                       ) : (
648 |                         // When not searching, show all children (includes Options and other components like Button)
649 |                         <>
650 |                           {children}
651 |                           {options.size === 0 && <div>{emptyListNode}</div>}
652 |                         </>
653 |                       )}
654 |                     </div>
655 |                   </div>
656 |                 </PopoverContent>
657 |               </Portal>
658 |             )}
659 |           </Popover>
660 |         </OptionTypeProvider>
661 |         {/* Hidden render to collect options when dropdown is closed */}
662 |         {!open && (
663 |           <div style={{ display: "none" }}>
664 |             <OptionTypeProvider Component={HiddenOption}>{children}</OptionTypeProvider>
665 |           </div>
666 |         )}
667 |       </OptionContext.Provider>
668 |     </SelectContext.Provider>
669 |   );
670 | });
671 | 
672 | // Visible option component for rendering items in the dropdown (used by OptionTypeProvider)
673 | function VisibleSelectOption(option: Option) {
674 |   const { value, label, enabled = true, children } = option;
675 |   const { onOptionAdd } = useOption();
676 |   const {
677 |     value: selectedValue,
678 |     onChange,
679 |     multiSelect,
680 |     readOnly,
681 |     setOpen,
682 |     highlightedValue,
683 |     optionRenderer,
684 |   } = useSelect();
685 | 
686 |   const optionRef = useRef<HTMLDivElement>(null);
687 | 
688 |   const opt: Option = useMemo(() => {
689 |     return {
690 |       ...option,
691 |       label: label ?? "",
692 |       keywords: option.keywords || [label ?? ""],
693 |     };
694 |   }, [option, label]);
695 | 
696 |   useEffect(() => {
697 |     onOptionAdd(opt);
698 |     // Don't remove options when component unmounts - they should persist
699 |   }, [opt, onOptionAdd]);
700 | 
701 |   const selected = useMemo(() => {
702 |     return Array.isArray(selectedValue) && multiSelect
703 |       ? selectedValue.map((v) => String(v)).includes(value)
704 |       : String(selectedValue) === String(value);
705 |   }, [selectedValue, value, multiSelect]);
706 | 
707 |   const isHighlighted = useMemo(() => {
708 |     return highlightedValue !== undefined && String(highlightedValue) === String(value);
709 |   }, [highlightedValue, value]);
710 | 
711 |   // Scroll into view when highlighted
712 |   useEffect(() => {
713 |     if (isHighlighted && optionRef.current) {
714 |       optionRef.current.scrollIntoView({ block: "nearest", behavior: "smooth" });
715 |     }
716 |   }, [isHighlighted]);
717 | 
718 |   const handleClick = () => {
719 |     if (readOnly) {
720 |       setOpen(false);
721 |       return;
722 |     }
723 |     if (enabled) {
724 |       onChange(value);
725 |     }
726 |   };
727 | 
728 |   return (
729 |     <div
730 |       ref={optionRef}
731 |       role="option"
732 |       aria-disabled={!enabled}
733 |       aria-selected={selected}
734 |       className={classnames(styles.multiSelectOption, {
735 |         [styles.disabledOption]: !enabled,
736 |         [styles.highlighted]: isHighlighted,
737 |       })}
738 |       onMouseDown={(e) => {
739 |         e.preventDefault();
740 |         e.stopPropagation();
741 |       }}
742 |       onClick={handleClick}
743 |       data-state={selected ? "checked" : undefined}
744 |     >
745 |       <div className={styles.multiSelectOptionContent}>
746 |         {optionRenderer ? (
747 |           optionRenderer({ label, value, enabled }, selectedValue as any, false)
748 |         ) : (
749 |           <>
750 |             {children || label}
751 |             {selected && <Icon name="checkmark" />}
752 |           </>
753 |         )}
754 |       </div>
755 |     </div>
756 |   );
757 | }
758 | 
759 | // Internal option component for rendering items in the dropdown
760 | function SelectOptionItem(option: Option & { isHighlighted?: boolean; itemIndex?: number }) {
761 |   const {
762 |     value,
763 |     label,
764 |     enabled = true,
765 |     readOnly,
766 |     children,
767 |     isHighlighted = false,
768 |     itemIndex,
769 |   } = option;
770 |   const {
771 |     value: selectedValue,
772 |     onChange,
773 |     multiSelect,
774 |     setOpen,
775 |     setSelectedIndex,
776 |     optionRenderer,
777 |   } = useSelect();
778 | 
779 |   const optionRef = useRef<HTMLDivElement>(null);
780 | 
781 |   const selected = useMemo(() => {
782 |     return Array.isArray(selectedValue) && multiSelect
783 |       ? selectedValue.map((v) => String(v)).includes(value)
784 |       : String(selectedValue) === String(value);
785 |   }, [selectedValue, value, multiSelect]);
786 | 
787 |   // Scroll into view when highlighted
788 |   useEffect(() => {
789 |     if (isHighlighted && optionRef.current) {
790 |       optionRef.current.scrollIntoView({ block: "nearest", behavior: "smooth" });
791 |     }
792 |   }, [isHighlighted]);
793 | 
794 |   const handleClick = () => {
795 |     if (readOnly) {
796 |       setOpen(false);
797 |       return;
798 |     }
799 |     if (enabled) {
800 |       onChange(value);
801 |     }
802 |   };
803 | 
804 |   return (
805 |     <div
806 |       ref={optionRef}
807 |       role="option"
808 |       aria-disabled={!enabled}
809 |       aria-selected={selected}
810 |       className={classnames(styles.multiSelectOption, {
811 |         [styles.disabledOption]: !enabled,
812 |         [styles.highlighted]: isHighlighted,
813 |       })}
814 |       onMouseDown={(e) => {
815 |         e.preventDefault();
816 |         e.stopPropagation();
817 |       }}
818 |       onMouseEnter={() => {
819 |         if (itemIndex !== undefined && setSelectedIndex && enabled) {
820 |           setSelectedIndex(itemIndex);
821 |         }
822 |       }}
823 |       onClick={handleClick}
824 |       data-state={selected ? "checked" : undefined}
825 |     >
826 |       <div className={styles.multiSelectOptionContent}>
827 |         {optionRenderer ? (
828 |           optionRenderer({ label, value, enabled }, selectedValue as any, false)
829 |         ) : (
830 |           <>
831 |             {children || label}
832 |             {selected && <Icon name="checkmark" />}
833 |           </>
834 |         )}
835 |       </div>
836 |     </div>
837 |   );
838 | }
839 | 
```

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

```typescript
  1 | import { getBounds, overflows, scaleByPercent } from "../../testing/component-test-helpers";
  2 | import { test, expect } from "../../testing/fixtures";
  3 | 
  4 | // =============================================================================
  5 | // BASIC FUNCTIONALITY TESTS
  6 | // =============================================================================
  7 | 
  8 | test.describe("Basic functionality", () => {
  9 |   test("component renders with default props", async ({ page, initTestBed }) => {
 10 |     await initTestBed(`
 11 |       <FlowLayout>
 12 |         <Text testId="item1">Item 1</Text>
 13 |         <Text testId="item2">Item 2</Text>
 14 |         <Text testId="item3">Item 3</Text>
 15 |       </FlowLayout>
 16 |     `);
 17 | 
 18 |     // Check that the component is visible
 19 |     const item1 = page.getByTestId("item1");
 20 |     const rect1 = await item1.boundingBox();
 21 |     const item2 = page.getByTestId("item2");
 22 |     const rect2 = await item2.boundingBox();
 23 |     const item3 = page.getByTestId("item3");
 24 |     const rect3 = await item3.boundingBox();
 25 | 
 26 |     // Check that children are rendered
 27 |     expect(rect1.height).toBe(rect2.height);
 28 |     expect(rect2.height).toBe(rect3.height);
 29 |     expect(rect1.width).toBeGreaterThan(0);
 30 |     expect(rect2.width).toBeGreaterThan(0);
 31 |     expect(rect3.width).toBeGreaterThan(0);
 32 |     expect(rect1.x).toBe(rect2.x);
 33 |     expect(rect2.x).toBe(rect3.x);
 34 |     expect(rect1.y).toBeLessThan(rect2.y);
 35 |     expect(rect2.y).toBeLessThan(rect3.y);
 36 |   });
 37 | 
 38 |   test("component renders when widths specified", async ({ page, initTestBed }) => {
 39 |     await initTestBed(`
 40 |       <FlowLayout>
 41 |         <Text testId="item1" width="80px">Item 1</Text>
 42 |         <Text testId="item2" width="80px">Item 2</Text>
 43 |         <Text testId="item3" width="80px">Item 3</Text>
 44 |       </FlowLayout>
 45 |     `);
 46 | 
 47 |     const item1 = page.getByTestId("item1");
 48 |     const rect1 = await item1.boundingBox();
 49 |     const item2 = page.getByTestId("item2");
 50 |     const rect2 = await item2.boundingBox();
 51 |     const item3 = page.getByTestId("item3");
 52 |     const rect3 = await item3.boundingBox();
 53 | 
 54 |     // Check that children are rendered
 55 |     expect(rect1.height).toBe(rect2.height);
 56 |     expect(rect2.height).toBe(rect3.height);
 57 |     expect(rect1.width).toBeLessThanOrEqual(80);
 58 |     expect(rect2.width).toBeLessThanOrEqual(80);
 59 |     expect(rect3.width).toBeLessThanOrEqual(80);
 60 |     expect(rect1.y).toBe(rect2.y);
 61 |     expect(rect2.y).toBe(rect3.y);
 62 |     expect(rect1.x).toBeLessThan(rect2.x);
 63 |     expect(rect2.x).toBeLessThan(rect3.x);
 64 |   });
 65 | 
 66 |   test("component wraps items when they exceed container width", async ({ page, initTestBed }) => {
 67 |     await initTestBed(`
 68 |       <FlowLayout width="280px">
 69 |         <Text testId="item1" width="100px">Item 1</Text>
 70 |         <Text testId="item2" width="100px">Item 2</Text>
 71 |         <Text testId="item3" width="100px">Item 3</Text>
 72 |       </FlowLayout>
 73 |     `);
 74 | 
 75 |     const item1 = page.getByTestId("item1");
 76 |     const rect1 = await item1.boundingBox();
 77 |     const item2 = page.getByTestId("item2");
 78 |     const rect2 = await item2.boundingBox();
 79 |     const item3 = page.getByTestId("item3");
 80 |     const rect3 = await item3.boundingBox();
 81 | 
 82 |     // Check that children are rendered
 83 |     expect(rect1.height).toBe(rect2.height);
 84 |     expect(rect2.height).toBe(rect3.height);
 85 |     expect(rect1.y).toBe(rect2.y);
 86 |     expect(rect2.y).toBeLessThan(rect3.y);
 87 |     expect(rect1.x).toBeLessThan(rect2.x);
 88 |     expect(rect1.x).toBe(rect3.x);
 89 |   });
 90 | 
 91 |   test("component applies gap correctly (#1)", async ({ page, initTestBed }) => {
 92 |     await initTestBed(`
 93 |       <FlowLayout gap="13px">
 94 |         <Stack testId="item1" width="100px">Item 1</Stack>
 95 |         <Stack testId="item2" width="100px">Item 2</Stack>
 96 |         <Stack testId="item3" width="100px">Item 3</Stack>
 97 |       </FlowLayout>
 98 |     `);
 99 | 
100 |     const item1 = page.getByTestId("item1");
101 |     const rect1 = await item1.boundingBox();
102 |     const item2 = page.getByTestId("item2");
103 |     const rect2 = await item2.boundingBox();
104 |     const item3 = page.getByTestId("item3");
105 |     const rect3 = await item3.boundingBox();
106 | 
107 |     // Check that children are rendered
108 |     expect(rect1.height).toBe(rect2.height);
109 |     expect(rect2.height).toBe(rect3.height);
110 |     expect(rect1.y).toBe(rect2.y);
111 |     expect(rect2.y).toBe(rect3.y);
112 |     expect(rect2.x - (rect1.x + rect1.width)).toBe(13);
113 |     expect(rect3.x - (rect2.x + rect2.width)).toBe(13);
114 |   });
115 | 
116 |   test("component applies gap correctly (#2)", async ({ page, initTestBed }) => {
117 |     await initTestBed(`
118 |       <FlowLayout gap="13px">
119 |         <Stack testId="item1">Item 1</Stack>
120 |         <Stack testId="item2">Item 2</Stack>
121 |         <Stack testId="item3">Item 3</Stack>
122 |       </FlowLayout>
123 |     `);
124 | 
125 |     const item1 = page.getByTestId("item1");
126 |     const rect1 = await item1.boundingBox();
127 |     const item2 = page.getByTestId("item2");
128 |     const rect2 = await item2.boundingBox();
129 |     const item3 = page.getByTestId("item3");
130 |     const rect3 = await item3.boundingBox();
131 | 
132 |     // Check that children are rendered
133 |     expect(rect1.height).toBe(rect2.height);
134 |     expect(rect2.height).toBe(rect3.height);
135 |     expect(rect2.y - (rect1.y + rect1.height)).toBe(13);
136 |     expect(rect3.y - (rect2.y + rect2.height)).toBe(13);
137 |   });
138 | 
139 |   test("component applies columnGap correctly", async ({ page, initTestBed }) => {
140 |     await initTestBed(`
141 |       <FlowLayout columnGap="13px" gap="40px">
142 |         <Stack testId="item1" width="100px">Item 1</Stack>
143 |         <Stack testId="item2" width="100px">Item 2</Stack>
144 |         <Stack testId="item3" width="100px">Item 3</Stack>
145 |       </FlowLayout>
146 |     `);
147 | 
148 |     const item1 = page.getByTestId("item1");
149 |     const rect1 = await item1.boundingBox();
150 |     const item2 = page.getByTestId("item2");
151 |     const rect2 = await item2.boundingBox();
152 |     const item3 = page.getByTestId("item3");
153 |     const rect3 = await item3.boundingBox();
154 | 
155 |     // Check that children are rendered
156 |     expect(rect1.height).toBe(rect2.height);
157 |     expect(rect2.height).toBe(rect3.height);
158 |     expect(rect1.y).toBe(rect2.y);
159 |     expect(rect2.y).toBe(rect3.y);
160 |     expect(rect2.x - (rect1.x + rect1.width)).toBe(13);
161 |     expect(rect3.x - (rect2.x + rect2.width)).toBe(13);
162 |   });
163 | 
164 |   test("component applies rowGap correctly", async ({ page, initTestBed }) => {
165 |     await initTestBed(`
166 |       <FlowLayout rowGap="13px" gap="40px">
167 |         <Stack testId="item1">Item 1</Stack>
168 |         <Stack testId="item2">Item 2</Stack>
169 |         <Stack testId="item3">Item 3</Stack>
170 |       </FlowLayout>
171 |     `);
172 | 
173 |     const item1 = page.getByTestId("item1");
174 |     const rect1 = await item1.boundingBox();
175 |     const item2 = page.getByTestId("item2");
176 |     const rect2 = await item2.boundingBox();
177 |     const item3 = page.getByTestId("item3");
178 |     const rect3 = await item3.boundingBox();
179 | 
180 |     // Check that children are rendered
181 |     expect(rect1.height).toBe(rect2.height);
182 |     expect(rect2.height).toBe(rect3.height);
183 |     expect(rect2.y - (rect1.y + rect1.height)).toBe(13);
184 |     expect(rect3.y - (rect2.y + rect2.height)).toBe(13);
185 |   });
186 | 
187 |   test("component applies rowGap and columnGap correctly", async ({ page, initTestBed }) => {
188 |     await initTestBed(`
189 |       <FlowLayout width="280px" columnGap="13px" rowGap="19px" gap="40px">
190 |         <Stack testId="item1" width="100px">Item 1</Stack>
191 |         <Stack testId="item2" width="100px">Item 2</Stack>
192 |         <Stack testId="item3" width="100px">Item 3</Stack>
193 |       </FlowLayout>
194 |     `);
195 | 
196 |     const item1 = page.getByTestId("item1");
197 |     const rect1 = await item1.boundingBox();
198 |     const item2 = page.getByTestId("item2");
199 |     const rect2 = await item2.boundingBox();
200 |     const item3 = page.getByTestId("item3");
201 |     const rect3 = await item3.boundingBox();
202 | 
203 |     // Check that children are rendered
204 |     expect(rect1.height).toBe(rect2.height);
205 |     expect(rect2.height).toBe(rect3.height);
206 |     expect(rect1.y).toBe(rect2.y);
207 |     expect(rect2.y).toBeLessThan(rect3.y);
208 |     expect(rect1.x).toBeLessThan(rect2.x);
209 |     expect(rect2.x - (rect1.x + rect1.width)).toBe(13);
210 |     expect(rect3.x).toBe(rect1.x);
211 |     expect(rect3.y - (rect2.y + rect2.height)).toBe(19);
212 |   });
213 | });
214 | 
215 | // =============================================================================
216 | // EDGE CASE TESTS
217 | // =============================================================================
218 | 
219 | test.describe("Edge cases", () => {
220 |   test("component handles empty content gracefully", async ({ page, initTestBed }) => {
221 |     await initTestBed(`<FlowLayout testId="flowLayout"></FlowLayout>`);
222 | 
223 |     const layout = page.getByTestId("flowLayout");
224 |     await expect(layout).toBeAttached();
225 |     await expect(layout).toBeEmpty();
226 |   });
227 | 
228 |   test("component handles very long items correctly (#1)", async ({ page, initTestBed }) => {
229 |     await initTestBed(`
230 |       <FlowLayout width="200px">
231 |         <Text testId="item1">This is a very long item that should wrap to the next line because it's too long</Text>
232 |         <Text testId="item2">Short item</Text>
233 |       </FlowLayout>
234 |     `);
235 | 
236 |     const item1 = page.getByTestId("item1");
237 |     const rect1 = await item1.boundingBox();
238 |     const item2 = page.getByTestId("item2");
239 |     const rect2 = await item2.boundingBox();
240 | 
241 |     expect(rect2.y).toBeGreaterThan(rect1.y + rect1.height);
242 |   });
243 | 
244 |   test("component handles very long items correctly (#2)", async ({ page, initTestBed }) => {
245 |     await initTestBed(`
246 |       <FlowLayout width="200px">
247 |         <Text testId="item1">Short item</Text>
248 |         <Text testId="item2">This is a very long item that should wrap to the next line because it's too long</Text>
249 |       </FlowLayout>
250 |     `);
251 | 
252 |     const item1 = page.getByTestId("item1");
253 |     const rect1 = await item1.boundingBox();
254 |     const item2 = page.getByTestId("item2");
255 |     const rect2 = await item2.boundingBox();
256 | 
257 |     expect(rect2.y).toBeGreaterThan(rect1.y + rect1.height);
258 |   });
259 | 
260 |   const PAGE_WIDTH = 1280;
261 | 
262 |   test("1 item 25% width", async ({ page, initTestBed }) => {
263 |     const itemHeight = "64px";
264 |     const itemWidthPercent = "25%";
265 |     await initTestBed(`
266 |       <FlowLayout width="${PAGE_WIDTH}">
267 |         <Stack testId="item" backgroundColor="red" height="${itemHeight}" width="${itemWidthPercent}"/>
268 |       </FlowLayout>
269 |     `);
270 |     const expectedWidth = scaleByPercent(PAGE_WIDTH, itemWidthPercent);
271 |     const { width: itemWidth } = await getBounds(page.getByTestId("item"));
272 |     expect(itemWidth).toEqual(expectedWidth);
273 |   });
274 | 
275 |   // gap should be ignored because of 1 item
276 |   test("1 item 25% width + gap", async ({ page, initTestBed }) => {
277 |     const itemHeight = "64px";
278 |     const itemWidthPercent = "25%";
279 |     const gap = "26px";
280 |     await initTestBed(`
281 |       <FlowLayout gap="${gap}" width="${PAGE_WIDTH}">
282 |         <Stack testId="item" backgroundColor="red" height="${itemHeight}" width="${itemWidthPercent}"/>
283 |       </FlowLayout>
284 |     `);
285 | 
286 |     const { right } = await getBounds(page.getByTestId("item"));
287 |     const expectedWidth = scaleByPercent(PAGE_WIDTH, itemWidthPercent);
288 |     expect(right).toEqual(expectedWidth);
289 |   });
290 | 
291 |   // gap should be ignored because of 1 item
292 |   test("1 item 100% width + gap", async ({ page, initTestBed }) => {
293 |     const itemHeight = "64px";
294 |     const itemWidthPercent = "100%";
295 |     const gap = "26px";
296 | 
297 |     await initTestBed(`
298 |       <FlowLayout gap="${gap}" width="${PAGE_WIDTH}">
299 |         <Stack testId="item" backgroundColor="red" height="${itemHeight}" width="${itemWidthPercent}"/>
300 |       </FlowLayout>
301 |     `);
302 |     const { width } = await getBounds(page.getByTestId("item"));
303 |     const expectedWidth = PAGE_WIDTH;
304 |     expect(width).toEqual(expectedWidth);
305 |   });
306 | 
307 |   test("4 item 25% width", async ({ page, initTestBed }) => {
308 |     const layoutWidth = "400px";
309 |     const itemWidthPercent = "25%";
310 |     const itemHeight = 64;
311 | 
312 |     await initTestBed(`
313 |       <FlowLayout testId="layout" width="${layoutWidth}" backgroundColor="cyan">
314 |         <Stack backgroundColor="red" height="${itemHeight}px" width="${itemWidthPercent}"/>
315 |         <Stack backgroundColor="green" height="${itemHeight}px" width="${itemWidthPercent}"/>
316 |         <Stack backgroundColor="blue" height="${itemHeight}px" width="${itemWidthPercent}"/>
317 |         <Stack backgroundColor="yellow" height="${itemHeight}px" width="${itemWidthPercent}"/>
318 |       </FlowLayout>
319 |     `);
320 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
321 | 
322 |     expect(layoutHeight).toEqual(itemHeight);
323 |   });
324 | 
325 |   test("3 item 25% width, 1 item 25.1% wraps", async ({ page, initTestBed }) => {
326 |     const itemHeight = 64;
327 |     const itemWidthPercent = "25%";
328 |     const itemWidthPercentBigger = "25.1%";
329 |     await initTestBed(`
330 |       <FlowLayout testId="layout" width="${PAGE_WIDTH}" gap="0">
331 |         <Stack backgroundColor="red" height="${itemHeight}px" width="${itemWidthPercent}"/>
332 |         <Stack backgroundColor="green" height="${itemHeight}px" width="${itemWidthPercent}"/>
333 |         <Stack backgroundColor="blue" height="${itemHeight}px" width="${itemWidthPercent}"/>
334 |         <Stack backgroundColor="yellow" height="${itemHeight}px" width="${itemWidthPercentBigger}"/>
335 |       </FlowLayout>
336 |     `);
337 | 
338 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
339 |     const expectedHeight = itemHeight * 2;
340 | 
341 |     expect(layoutHeight).toEqual(expectedHeight);
342 |   });
343 | 
344 |   // When gap is specified and wrapping, a horizontal gap is applied
345 |   test("wrap with gaps", async ({ page, initTestBed }) => {
346 |     const layoutWidth = 400;
347 |     const itemWidthPercent = "25%";
348 |     const itemWidthPercentBigger = "25.1%";
349 |     const itemHeight = 64;
350 |     const gap = 20;
351 |     await initTestBed(`
352 |       <FlowLayout testId="layout" width="${layoutWidth}px" backgroundColor="cyan" gap="${gap}px">
353 |         <Stack backgroundColor="red" height="${itemHeight}px" width="${itemWidthPercentBigger}"/>
354 |         <Stack backgroundColor="green" height="${itemHeight}px" width="${itemWidthPercent}"/>
355 |         <Stack backgroundColor="blue" height="${itemHeight}px" width="${itemWidthPercent}"/>
356 |         <Stack backgroundColor="yellow" height="${itemHeight}px" width="${itemWidthPercent}"/>
357 |       </FlowLayout>
358 |     `);
359 | 
360 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
361 |     const expectedHeight = gap + itemHeight * 2;
362 | 
363 |     expect(layoutHeight).toEqual(expectedHeight);
364 |   });
365 | 
366 |   test("item with * width fills row", async ({ page, initTestBed }) => {
367 |     const itemHeight = 64;
368 |     const itemWidth = "*";
369 |     await initTestBed(`
370 |       <FlowLayout testId="layout">
371 |         <Stack backgroundColor="red" height="${itemHeight}px" width="50"/>
372 |         <Stack backgroundColor="green" height="${itemHeight}px" width="50"/>
373 |         <Stack testId="item2" backgroundColor="blue" height="${itemHeight}px" width="${itemWidth}"/>
374 |       </FlowLayout>
375 |     `);
376 | 
377 |     const { height: layoutHeight, right: layoutRight } = await getBounds(
378 |       page.getByTestId("layout"),
379 |     );
380 |     const { right: starItemRight } = await getBounds(page.getByTestId("item2"));
381 | 
382 |     expect(layoutHeight).toEqual(itemHeight);
383 |     expect(layoutRight).toEqual(starItemRight);
384 |   });
385 | 
386 |   // rowGap applies when wrapping
387 |   test("wrap with rowGap", async ({ page, initTestBed }) => {
388 |     const itemHeight = 64;
389 |     const itemWidthPercent = "50%";
390 |     const rowGap = 24;
391 |     await initTestBed(`
392 |   <FlowLayout testId="layout" rowGap="${rowGap}px">
393 |     <Stack backgroundColor="red" height="${itemHeight}px" width="${itemWidthPercent}"/>
394 |     <Stack backgroundColor="green" height="${itemHeight}px" width="${itemWidthPercent}"/>
395 |     <Stack backgroundColor="blue" height="${itemHeight}px" width="${itemWidthPercent}"/>
396 |     <Stack backgroundColor="yellow" height="${itemHeight}px" width="${itemWidthPercent}"/>
397 |   </FlowLayout>
398 |   `);
399 | 
400 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
401 |     const expectedHeight = itemHeight * 2 + rowGap;
402 | 
403 |     expect(layoutHeight).toEqual(expectedHeight);
404 |   });
405 | 
406 |   test("wrap with columnGap", async ({ page, initTestBed }) => {
407 |     const itemWidth = 200;
408 |     const columnGap = 24;
409 |     const layoutWidth = itemWidth + columnGap + itemWidth;
410 |     const itemHeight = 64;
411 |     await initTestBed(`
412 |   <FlowLayout testId="layout" width="${layoutWidth}px" columnGap="${columnGap}px">
413 |     <Stack backgroundColor="red" height="${itemHeight}px" width="${itemWidth}px"/>
414 |     <Stack backgroundColor="green" height="${itemHeight}px" width="${itemWidth}px"/>
415 |     <Stack backgroundColor="blue" height="${itemHeight}px" width="${itemWidth}px"/>
416 |     <Stack backgroundColor="yellow" testId="item3" height="${itemHeight}px" width="${itemWidth}px"/>
417 |   </FlowLayout>
418 |   `);
419 | 
420 |     const { left: item3Left } = await getBounds(page.getByTestId("item3"));
421 |     const expectedItem3Left = itemWidth + columnGap;
422 | 
423 |     expect(item3Left).toEqual(expectedItem3Left);
424 |   });
425 | 
426 |   // wrapping: columnGap & rowGap overrules gap prop
427 |   test("columnGap & rowGap overrules gap", async ({ page, initTestBed }) => {
428 |     const itemWidth = 200;
429 |     const columnGap = 24;
430 |     const layoutWidth = itemWidth + columnGap + itemWidth;
431 |     const itemHeight = 64;
432 |     const rowGap = 24;
433 |     const gap = 5;
434 |     await initTestBed(`
435 |   <FlowLayout testId="layout" width="${layoutWidth}px" gap="${gap}px" columnGap="${columnGap}px" rowGap="${rowGap}px">
436 |     <Stack testId="item0" backgroundColor="red" height="${itemHeight}px" width="${itemWidth}px"/>
437 |     <Stack testId="item1" backgroundColor="green" height="${itemHeight}px" width="${itemWidth}px"/>
438 |     <Stack testId="item2" backgroundColor="blue" height="${itemHeight}px" width="${itemWidth}px"/>
439 |     <Stack testId="item3" backgroundColor="yellow" height="${itemHeight}px" width="${itemWidth}px"/>
440 |   </FlowLayout>
441 |   `);
442 | 
443 |     const { left: item3Left } = await getBounds(page.getByTestId("item3"));
444 |     const expectedItem3Left = itemWidth + columnGap;
445 | 
446 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
447 |     const expectedLayoutHeight = itemHeight + rowGap + itemHeight;
448 | 
449 |     expect(item3Left).toEqual(expectedItem3Left);
450 |     expect(layoutHeight).toEqual(expectedLayoutHeight);
451 |   });
452 | 
453 |   // 4 items with 25% each perfectly fit in one row
454 |   // gaps, borders, margins don't count when breaking into new lines
455 |   test("no wrap from gap, border, margin", async ({ page, initTestBed }) => {
456 |     const itemHeight = 64;
457 |     const width = "25%";
458 |     const marginInline = 100;
459 |     await initTestBed(`
460 |   <FlowLayout testId="layout" gap="26px" >
461 |     <Stack testId="item0" border="solid 6px black" marginRight="${marginInline}px" marginLeft="${marginInline}px" backgroundColor="red" height="${itemHeight}px" width="${width}px"/>
462 |     <Stack testId="item1" border="solid 6px black" marginRight="${marginInline}px" marginLeft="${marginInline}px" backgroundColor="green" height="${itemHeight}px" width="${width}px"/>
463 |     <Stack testId="item2" border="solid 6px black" marginRight="${marginInline}px" marginLeft="${marginInline}px" backgroundColor="blue" height="${itemHeight}px" width="${width}px"/>
464 |     <Stack testId="item3" border="solid 6px black" marginRight="${marginInline}px" marginLeft="${marginInline}px" backgroundColor="yellow" height="${itemHeight}px" width="${width}px"/>
465 |   </FlowLayout>
466 |   `);
467 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
468 |     expect(layoutHeight).toEqual(itemHeight);
469 |   });
470 | 
471 |   // Elements will cap at 100% width
472 |   test("no horizontal overflow", async ({ page, initTestBed }) => {
473 |     const itemHeight = 64;
474 |     const bigWidths = { percent: "120%", px: "1000000000px", em: "1000000000em" };
475 |     await initTestBed(`
476 |   <FlowLayout testId="layout" gap="0">
477 |     <Stack testId="item0" height="${itemHeight}px" width="${bigWidths.percent}"/>
478 |     <Stack testId="item1" height="${itemHeight}px" width="${bigWidths.px}"/>
479 |     <Stack testId="item2" height="${itemHeight}px" width="${bigWidths.em}"/>
480 |   </FlowLayout>
481 |   `);
482 | 
483 |     const isOverflownItem0 = await overflows(page.getByTestId("item0"), "x");
484 |     const isOverflownItem1 = await overflows(page.getByTestId("item1"), "x");
485 |     const isOverflownItem2 = await overflows(page.getByTestId("item2"), "x");
486 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
487 |     const expectedLayoutHeight = itemHeight * 3;
488 | 
489 |     expect(isOverflownItem0).toStrictEqual(false);
490 |     expect(isOverflownItem1).toStrictEqual(false);
491 |     expect(isOverflownItem2).toStrictEqual(false);
492 |     expect(layoutHeight).toEqual(expectedLayoutHeight);
493 |   });
494 | 
495 |   // SpaceFillers can be used to break into new lines
496 |   test("SpaceFiller adds line break", async ({ page, initTestBed }) => {
497 |     const itemHeight = 64;
498 |     const itemWidth = "20%";
499 |     await initTestBed(`
500 |   <FlowLayout testId="layout" gap="0">
501 |     <Stack backgroundColor="red" height="${itemHeight}px" width="${itemWidth}"/>
502 |     <Stack backgroundColor="blue" height="${itemHeight}px" width="${itemWidth}"/>
503 |     <SpaceFiller/>
504 |     <Stack backgroundColor="green" height="${itemHeight}px" width="${itemWidth}"/>
505 |   </FlowLayout>
506 |   `);
507 | 
508 |     const expectedHeight = 2 * itemHeight;
509 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
510 | 
511 |     expect(layoutHeight).toEqual(expectedHeight);
512 |   });
513 | 
514 |   // The layout properly handles overflow on the Y axis
515 |   // The scrollbar must not overlap the rightmost element
516 |   test("scrollbar on overflow Y", async ({ page, initTestBed }) => {
517 |     const itemHeight = 128;
518 |     await initTestBed(`
519 |   <FlowLayout testId="layout" height="100px" columnGap="10px" overflowY="auto">
520 |     <Stack backgroundColor="red" height="${itemHeight}px" border="solid 8px black"/>
521 |     <Stack backgroundColor="blue" height="${itemHeight}px"/>
522 |     <Stack backgroundColor="green" height="${itemHeight}px"/>
523 |   </FlowLayout>
524 |   `);
525 | 
526 |     const result = await overflows(page.getByTestId("layout"), "y");
527 |     expect(result).toEqual(true);
528 |   });
529 | 
530 |   // The layout properly handles overflow on the Y axis
531 |   // The scrollbar must not overlap the rightmost element
532 |   test("scrollbar on overflow Y multi items", async ({ page, initTestBed }) => {
533 |     const itemHeight = 128;
534 |     await initTestBed(`
535 |   <FlowLayout testId="layout" height="100px" overflowY="auto">
536 |     <Stack backgroundColor="red" height="${itemHeight}px" border="solid 8px black" width="50%"/>
537 |     <Stack backgroundColor="blue" height="${itemHeight}px" border="solid 8px black" width="50%"/>
538 |     <Stack backgroundColor="green" height="${itemHeight}px"/>
539 |   </FlowLayout>
540 |   `);
541 | 
542 |     const result = await overflows(page.getByTestId("layout"), "y");
543 | 
544 |     expect(result).toEqual(true);
545 |   });
546 | 
547 |   test("multiple star sized next to each other doesn't break", async ({
548 |     page,
549 |     initTestBed,
550 |   }) => {
551 |     await initTestBed(`
552 |     <FlowLayout testId="layout" width="100px" columnGap="10px">
553 |       <Stack testId="red" backgroundColor="red" height="10px" width="20px"/>
554 |       <Stack testId="green" backgroundColor="green" height="10px" width="*"/>
555 |       <Stack testId="blue" backgroundColor="blue" height="10px" width="2*"/>
556 |     </FlowLayout>`);
557 | 
558 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
559 |     const { width: redWidth } = await getBounds(page.getByTestId("red"));
560 |     const { width: greenWidth } = await getBounds(page.getByTestId("green"));
561 |     const { width: blueWidth } = await getBounds(page.getByTestId("blue"));
562 | 
563 |     // red: 20px | 10px gap | green: 20px | 10px gap | blue: 40px
564 |     expect(layoutHeight).toEqual(10);
565 |     expect(redWidth).toEqual(20);
566 |     expect(greenWidth).toEqual(20);
567 |     expect(blueWidth).toEqual(40);
568 |   });
569 | 
570 |   test("SpaceFiller breaks star sized items", async ({ page, initTestBed }) => {
571 |     await initTestBed(`
572 |     <FlowLayout testId="layout" width="100px" gap="10px">
573 |       <Stack testId="red" backgroundColor="red" height="10px" width="20px"/>
574 |       <Stack testId="green" backgroundColor="green" height="10px" width="*"/>
575 |       <SpaceFiller/>
576 |       <Stack testId="blue" backgroundColor="blue" height="10px" width="2*"/>
577 |     </FlowLayout>`);
578 | 
579 |     const { height: layoutHeight } = await getBounds(page.getByTestId("layout"));
580 |     const { width: redWidth } = await getBounds(page.getByTestId("red"));
581 |     const { width: greenWidth } = await getBounds(page.getByTestId("green"));
582 |     const { width: blueWidth } = await getBounds(page.getByTestId("blue"));
583 | 
584 |     // red: 20px | 10px gap | green: 70px
585 |     // gap 10px
586 |     // blue: 100px
587 |     expect(layoutHeight).toEqual(30);
588 |     expect(redWidth).toEqual(20);
589 |     expect(greenWidth).toEqual(70);
590 |     expect(blueWidth).toEqual(100);
591 |   });
592 | });
593 | 
```

--------------------------------------------------------------------------------
/blog/public/blog/introducing-xmlui.md:
--------------------------------------------------------------------------------

```markdown
  1 | > [!TIP]
  2 | > Ed. note: This is the post that was discussed on [Hacker News](https://news.ycombinator.com/item?id=44625292).
  3 | 
  4 | In the mid-1990s you could create useful software without being an ace coder. You had Visual Basic, you had a rich ecosystem of components, you could wire them together to create apps, standing on the shoulders of the coders who built those components. If you're younger than 45 you may not know what that was like, nor realize web components have never worked the same way. The project we're announcing today, [XMLUI](https://xmlui.org), brings the VB model to the modern web and its React-based component ecosystem. XMLUI wraps React and CSS and provides a suite of components that you compose with XML markup. Here's a little app to check the status of London tube lines.
  5 | 
  6 | ```xml
  7 | <App>
  8 |   <Select id="lines" initialValue="bakerloo">
  9 |     <Items data="https://api.tfl.gov.uk/line/mode/tube/status">
 10 |     </Items>
 11 |   </Select>
 12 |   <DataSource
 13 |     id="tubeStations"
 14 |     url="https://api.tfl.gov.uk/Line/{lines.value}/Route/Sequence/inbound"
 15 |     resultSelector="stations"/>
 16 |   <Table data="{tubeStations}" height="280px">
 17 |     <Column bindTo="name" />
 18 |     <Column bindTo="modes" />
 19 |   </Table>
 20 | </App>
 21 | ```
 22 | 
 23 | ![XMLUI Demo](https://jonudell.net/xmlui/xmlui2.gif)
 24 | 
 25 | A dozen lines of XML is enough to:
 26 | 
 27 | - Define a [Select](https://docs.xmlui.org/components/Select) and fill its [Items](https://docs.xmlui.org/components/Items) with data from an API call.
 28 | - Define a [DataSource](https://docs.xmlui.org/components/DataSource) to fetch data from another API call.
 29 | - Use the value of the `Select` to dynamically form the URL of the `DataSource`.
 30 | - Use a [resultSelector](https://docs.xmlui.org/components/DataSource#resultselector) to drill into the result of the second API call.
 31 | - Bind that result to a [Table](https://docs.xmlui.org/components/Table).
 32 | - Bind fields in the result to [Columns](https://docs.xmlui.org/components/Column).
 33 | 
 34 | This is a clean, modern, component-based app that's [reactive](https://docs.xmlui.org/reactive-intro) and [themed](https://docs.xmlui.org/themes-intro) without requiring any knowledge of React or CSS. That's powerful leverage. And it's code you can read and maintain, no matter if it was you or an LLM assistant who wrote it. I'm consulting for the project so you should judge for yourself, but to me this feels like an alternative to the JavaScript industrial complex that ticks all the right boxes.
 35 | 
 36 | ## Components
 37 | 
 38 | My most-cited BYTE article was a 1994 cover story called [Componentware](https://web.archive.org/web/19961220155530/http://www.byte.com/art/9405/sec5/art1.htm). Many of us had assumed that the engine of widespread software reuse would be libraries of low-level objects linked into programs written by skilled coders. What actually gained traction were components built by professional developers and used by business developers.
 39 | 
 40 | There were Visual Basic components for charting, network communication, data access, audio/video playback, and image scanning/editing. UI controls included buttons, dialog boxes, sliders, grids for displaying and editing tabular data, text editors, tree and list and tab views. People used these controls to build point-of-sale systems, scheduling and project management tools, systems for medical and legal practice management, sales and inventory reporting, and much more.
 41 | 
 42 | That ecosystem of component producers and consumers didn't carry forward to the web. I'm a fan of web components but it's the React flavor that dominate and they are not accessible to the kind of developer who could productively use Visual Basic components back in the day. You have to be a skilled coder not only to create a React component but also to use one. XMLUI wraps React components so solution builders can use them.
 43 | 
 44 | ### User-defined components
 45 | 
 46 | XMLUI provides a deep [catalog of components](https://docs.xmlui.org/components/_overview) including all the interactive ones you'd expect as well as behind-the-scenes ones like `DataSource`, [APICall](https://docs.xmlui.org/components/APICall), and [Queue](https://docs.xmlui.org/components/Queue). You can easily define your own components that interop with the native set and with one another. Here's the markup for a `TubeStops` component.
 47 | 
 48 | ```xml
 49 | <Component name="TubeStops">
 50 |   <DataSource
 51 |     id="stops"
 52 |     url="https://api.tfl.gov.uk/Line/{$props.line}/StopPoints"
 53 |     transformResult="{window.transformStops}"
 54 |   />
 55 |   <Text variant="strong">{$props.line}</Text>
 56 |   <Table data="{stops}">
 57 |     <Column width="3*" bindTo="name" />
 58 |     <Column bindTo="zone" />
 59 |     <Column bindTo="wifi" >
 60 |       <Fragment when="{$item.wifi === 'yes'}">
 61 |         <Icon name="checkmark"/>
 62 |       </Fragment>
 63 |     </Column>
 64 |     <Column bindTo="toilets" >
 65 |       <Fragment when="{$item.toilets === 'yes'}">
 66 |         <Icon name="checkmark"/>
 67 |       </Fragment>
 68 |     </Column>
 69 |   </Table>
 70 | </Component>
 71 | ```
 72 | 
 73 | Here's markup that uses the component twice in a side-by-side layout.
 74 | 
 75 | ```xml
 76 | <HStack>
 77 |   <Stack width="50%">
 78 |     <TubeStops line="victoria" />
 79 |   </Stack>
 80 |   <Stack width="50%">
 81 |     <TubeStops line="waterloo-city" />
 82 |   </Stack>
 83 | </HStack>
 84 | ```
 85 | 
 86 | It's easy to read and maintain short snippets of XMLUI markup. When the markup grows to a hundred lines or more, not so much. But I never need to look at that much code; when components grow too large I refactor them. In any programming environment that maneuver entails overhead: you have to create and name files, identify which things to pass as properties from one place, and unpack them in another. But the rising LLM tide lifts all boats. Because I can delegate the refactoring to my team of AI assistants I'm able to do it fluidly and continuously. LLMs don't "know" about XMLUI out of the box but they do know about XML, and with the help of MCP (see below) they can "know" a lot about XMLUI specifically.
 87 | 
 88 | ## Reactivity
 89 | 
 90 | If you've never been a React programmer, as I have not, the biggest challenge with XMLUI-style reactivity isn't what you need to learn but rather what you need to unlearn. Let's take another look at the code for the app shown at the top of this post.
 91 | 
 92 | ```xml
 93 | <App>
 94 |   <Select id="lines" initialValue="bakerloo">
 95 |     <Items data="https://api.tfl.gov.uk/line/mode/tube/status">
 96 |         <Option value="{$item.id}" label="{$item.name}" />
 97 |     </Items>
 98 |   </Select>
 99 |   <DataSource
100 |     id="tubeStations"
101 |     url="https://api.tfl.gov.uk/Line/{lines.value}/Route/Sequence/inbound"
102 |     resultSelector="stations"/>
103 |   <Table data="{tubeStations}" height="280px">
104 |     <Column bindTo="name" />
105 |     <Column bindTo="modes" />
106 |   </Table>
107 | </App>
108 | ```
109 | 
110 | Note how the `Select` declares the property `id="lines"`. That makes `lines` a reactive variable.
111 | 
112 | Now look at the `url` property of the `DataSource`. It embeds a reference to `lines.value`. Changing the selection changes `lines.value`. The `DataSource` reacts by fetching a new batch of details. Likewise the `Table`'s `data` property refers to `tubeStations` (the `DataSource`) so it automatically displays the new data.
113 | 
114 | There's a name for this pattern: reactive data binding. It's what spreadsheets do when a change in one cell propagates to others that refer to it. And it's what React enables for web apps. React is a complex beast that only expert programmers can tame. Fortunately the expert programmers who build XMLUI have done that for you. As an XMLUI developer you may need to unlearn imperative habits in order to go with the declarative flow. It's a different mindset but if you keep the spreadsheet analogy in mind you'll soon get the hang of it. Along the way you'll likely discover happy surprises. For example, here's the search feature in our demo app, [XMLUI Invoice](https://github.com/xmlui-org/xmlui-invoice/).
115 | 
116 | ![Search demo](https://jonudell.net/xmlui/search2.gif)
117 | 
118 | Initially I wrote it in a conventional way, with a search button. Then I realized there was no need for a button. The `DataSource` URL that drives the query can react to keystrokes in the `TextBox`, and the `Table` can in turn react when the `DataSource` refreshes.
119 | 
120 | ```xml
121 | <Component name="SearchEverything">
122 |     <VStack paddingTop="$space-4">
123 |         <TextBox
124 |             placeholder="Enter search term..."
125 |             width="25rem"
126 |             id="searchTerm"
127 |         />
128 |         <Card when="{searchTerm.value}">
129 |             <DataSource
130 |               id="search"
131 |               url="/api/search/{searchTerm.value}"
132 |             />
133 |             <Text>Found {search.value ? search.value.length : 0} results for
134 |                 "{searchTerm.value}":</Text>
135 |             <Table data="{search}">
136 |                 <Column  bindTo="table_name" header="Type" width="100px" />
137 |                 <Column  bindTo="title" header="Title" width="*" />
138 |                 <Column  bindTo="snippet" header="Match Details" width="3*" />
139 |             </Table>
140 |         </Card>
141 |     </VStack>
142 | </Component>
143 | ```
144 | 
145 | ## Themes
146 | 
147 | When the team first showed me the XMLUI [theme system](https://docs.xmlui.org/themes-intro) I wasn't too excited. I am not a designer so I appreciate a nice default theme that doesn't require me to make color choices I'm not qualified to make. The ability to switch themes has never felt that important to me, and I've never quite understood why developer are so obsessed with dark mode. I have wrestled with CSS, though, to achieve both style and layout effects, and the results have not been impressive. XMLUI aims to make everything you build look good, and behave gracefully, without requiring you to write any CSS or CSS-like style and layout directives.
148 | 
149 | You can apply inline styles but for the most part you won't need them and shouldn't use them. For me this was another unlearning exercise. I know enough CSS to be dangerous and in the early going I abused inline styles. That was partly my fault and partly because LLMs think inline styles are catnip and will abuse them on your behalf. If you look at the code snippets here, though, you'll see almost no explicit style or layout directives. Each component provides an extensive set of theme variables that influence its text color and font, background color, margins, borders, paddings, and more. They follow a naming convention that enables a setting to control appearance globally or in progressively more granular ways. For example, here are the variables that can control the border color of a solid button using the primary color when the mouse hovers over it.
150 | 
151 | ```
152 | color-primary
153 | backgroundColor-Button
154 | backgroundColor-Button-solid
155 | backgroundColor-Button-primary
156 | backgroundColor-Button-primary-solid
157 | backgroundColor-Button-primary-solid--hover
158 | ```
159 | 
160 | When it renders a button, XMLUI works up the chain from the most specific setting to the most general. This arrangement gives designers many degrees of freedom to craft exquisitely detailed themes. But almost all the settings are optional, and those that are defined by default use logical names instead of hardcoded values. So, for example, the default setting for `backgroundColor-Button-primary` is `$color-primary-500`. That's the midpoint in a range of colors that play a primary role in the UI. There's a set of such semantic roles, each associated with a color palette. The key roles are:
161 | 
162 | **Surface**: creates neutral backgrounds and containers.
163 | 
164 | **Primary**: draws attention to important elements and actions.
165 | 
166 | **Secondary**: provides visual support without competing with primary elements.
167 | 
168 | What's more, you can generate complete palettes from single midpoint value for each.
169 | 
170 | ```yaml
171 | name: Earthtone
172 | id: earthtone
173 | themeVars:
174 |   color-primary: "hsl(30, 50%, 30%)"
175 |   color-secondary: "hsl(120, 40%, 25%)"
176 |   color-surface: "hsl(39, 43%, 97%)"
177 | ```
178 | 
179 | ![Earthtone theme](https://jonudell.info/xmlui/earthtone.png)
180 | 
181 | Themes aren't just about colors, though. XMLUI components work hard to provide default layout settings that yield good spacing, padding, and margins both within individual components and across a canvas that composes sets of them. I am, again, not a designer, so not really qualified to make a professional judgement about how it all works. But the effects I can achieve look pretty good to me.
182 | 
183 | ## Scripting
184 | 
185 | As a Visual Basic developer you weren't expected to be an ace coder but were expected to be able to handle a bit of scripting. It's the same with XMLUI. The language is JavaScript and you can go a long way with tiny snippets like this one in `TubeStops`.
186 | 
187 | ```xml
188 | <Fragment when="{$item.wifi === 'yes'}"></Fragment>
189 | ```
190 | 
191 | `TubeStops` does also use the `transformResult` property of its `DataSource` to invoke a more ambitious chunk of code.
192 | 
193 | ```javascript
194 | function transformStops(stops) {
195 |   return stops.map(stop => {
196 |     // Helper to extract a value from additionalProperties by key
197 |     const getProp = (key) => {
198 |       const prop = stop.additionalProperties && stop.additionalProperties.find(p => p.key === key);
199 |       return prop ? prop.value : '';
200 |     };
201 |     return {
202 |       name: stop.commonName,
203 |       zone: getProp('Zone'),
204 |       wifi: getProp('WiFi'),
205 |       toilets: getProp('Toilets'),
206 |       // A comma-separated list of line names that serve this stop
207 |       lines: stop.lines ? stop.lines.map(line => line.name).join(', ') : ''
208 |     };
209 |   });
210 | }
211 | ```
212 | 
213 | This is not trivial, but it's not rocket science either. And of course you don't need to write stuff like this nowadays, you can have an LLM assistant do it for you. So we can't claim that XMLUI is 100% declarative. But I think it's fair to say that the imperative parts are well-scoped and accessible to a solution builder who doesn't know, or want to know, anything about the JavaScript industrial complex.
214 | 
215 | ## Model Context Protocol
216 | 
217 | In the age of AI, who needs XMLUI when you can just have LLMs write React apps for you? It's a valid question and I think I have a pretty good answer. The first version of XMLUI Invoice was a React app that Claude wrote in 30 seconds. It was shockingly complete and functional. But I wasn't an equal partner in the process. I'm aware that React has things like `useEffect` and `useContext` but I don't really know what they are or how to use them properly, and am not competent to review or maintain JavaScript code that uses these patterns. The same disadvantage applies to the CSS that Claude wrote. If you're a happy vibe coder who never expects to look at or work with the code that LLMs generate, then maybe XMLUI isn't for you.
218 | 
219 | If you need to be able review and maintain your app, though, XMLUI levels the playing field. I can read, evaluate, and competently adjust the XMLUI code that LLMs write. In [a recent talk](https://www.youtube.com/watch?v=LCEmiRjPEtQ) Andrej Karpathy argues that the sweet spot for LLMS is a collaborative partnership in which we can dynamically adjust how much control we give them. The "autonomy slider" he envisions requires that we and our assistants operate in the same conceptual/semantic space. That isn't true for me, nor for the developers XMLUI aims to empower, if the space is React+CSS. It can be true if the space is XMLUI.
220 | 
221 | To enhance the collaboration we provide [an MCP server](https://github.com/xmlui-org/xmlui-mcp) that helps you direct agents' attention as you work with them on XMLUI apps. In [MCP is RSS for AI](https://thenewstack.io/mcp-is-rss-for-ai-more-use-cases-for-model-context-protocol/) I described the kinds of questions that agents like Claude and Cursor can use xmlui-mcp to ask and answer:
222 | 
223 | - Is there a component that does [X]?
224 | - What do the docs for [X] say about topic [Y]?
225 | - How does the source code implement [X]?
226 | - How is [X] is used in other apps?
227 | 
228 | You place the xmlui-mcp server alongside the xmlui repo which includes docs and source code. And the repo in which you are developing an XMLUI app. And, ideally, other repos that contain reference apps like XMLUI Invoice.
229 | 
230 | ### Working with LLMs
231 | 
232 | This arrangement has mostly exceeded my expectations. As I build out a suite of apps that exemplify best practices and patterns, the agentic collaboration improves. This flywheel effect is, of course, still subject to the peculiar habits of LLM assistants who constantly need to be reminded of the rules.
233 | 
234 | > 1. don't write any code without my permission, always preview proposed changes, discuss, and only proceed with approval.
235 | >
236 | > 2. don't add any xmlui styling, let the theme and layout engine do its job
237 | >
238 | > 3. proceed in small increments, write the absolute minimum amount of xmlui markup necessary and no script if possible
239 | >
240 | > 4. do not invent any xmlui syntax. only use constructs for which you can find examples in the docs and sample apps. cite your sources.
241 | >
242 | > 5. never touch the dom. we only use xmlui abstractions inside the App realm, with help from vars and functions defined on the window variable in index.html
243 | >
244 | > 6. keep complex functions and expressions out of xmlui, they can live in index.html or (if scoping requires) in code-behind
245 | >
246 | > 7. use the xmlui mcp server to list and show component docs but also search xmlui source, docs, and examples
247 | >
248 | > 8. always do the simplest thing possible
249 | 
250 | It's like working with 2-year-old savants. Crazy, but it can be effective!
251 | 
252 | To increase the odds that you'll collaborate effectively, we added a [How To](https://docs.xmlui.org/howto) section to the docs site. The MCP server makes these articles visible to agents by providing tools that list and search them. This was inspired by a friend who asked: "For a Select, suppose you don't have a static default first item but you want to fetch data and choose the first item from data as the default selected, how'd you do that in xmlui?" It took me a few minutes to put together an example. Then I realized that's the kind of question LLMs should be able to ask and answer autonomously. When an agent uses one of these tools, it is anchored to ground truth: an article found this way has a citable URL that points to a working example.
253 | 
254 | It's way easier for me to do things with XMLUI than with React and CSS, but I've also climbed a learning curve and absorbed a lot of tacit knowledge. Will the LLM-friendly documentation flatten the learning curve for newcomers and their AI assistants? I'm eager to find out.
255 | 
256 | ## Content management
257 | 
258 | We say XMLUI is for building apps, but what are apps really? Nowadays websites are often apps too, built on frameworks like Vercel's [Next.js](https://en.wikipedia.org/wiki/Next.js). I've used publishing systems built that way and I am not a fan. You shouldn't need a React-savvy front-end developer to help you make routine changes to your site. And with XMLUI you don't. Our [demo site](https://demo.xmlui.org), [docs site](https://docs.xmlui.org), and [landing page](https://xmlui.org) are all XMLUI apps that are much easier for me to write and maintain than the Next.js sites I've worked on.
259 | 
260 | "Eating the dogfood" is an ugly name for a beautiful idea: Builders should use and depend on the things they build. We do, but there's more to the story of XMLUI as a CMS. When you build an app with XMLUI you are going to want to document it. There's a nice synergy available: the app and its documentation can be made of [the same stuff](https://demo.coressh.com/ui/help). You can even showcase live demos of your app in your docs as we do in [component documentation](https://docs.xmlui.org/components/_overview), [tutorials](https://docs.xmlui.org/tutorial-01), and [How To](https://docs.xmlui.org/howto) articles.
261 | 
262 | I was an early proponent of screencasts for software demos, and it can certainly be better to show than tell, but it's infuriating to search for the way to do something and find only a video. Ideally you show and tell. Documenting software with a mix of code, narrative, and live interaction brings all the modalities together.
263 | 
264 | ## Extensibility
265 | 
266 | Out of the box, XMLUI wraps a bunch of React components. What happens when the one you need isn't included? This isn't my first rodeo. In a [previous effort](https://blog.jonudell.net/2023/06/14/radical-just-in-time-learning/) I leaned heavily on LLMs to dig through layers of React code but was still unable to achieve the wrapping I was aiming for.
267 | 
268 | For XMLUI the component I most wanted to include was the [Tiptap](https://tiptap.dev/) editor which is itself a wrapper around the foundational [ProseMirror](https://prosemirror.net/) toolkit. Accomplishing that was a stretch goal that I honestly didn't expect to achieve before release. But I was pleasantly surprised, and here is the proof.
269 | 
270 | ![Tiptap editor demo](https://jonudell.info/xmlui/tiptap.gif)
271 | 
272 | This XMLUI `TableEditor` is the subject of our [guide](https://docs.xmlui.org/build-editor-component) for developers who want to understand how to create an XMLUI component that wraps a React component. And isn't just a toy example. When you use XMLUI for publishing, the foundation is [Markdown](http://docs.xmlui.org/working-with-markdown) which is wonderful for writing and editing headings, paragraphs, lists, and code blocks, but awful for writing and editing tables. In that situation I always resort to a visual editor to produce Markdown table syntax. Now I have that visual editor as an XMLUI component that I can embed anywhere.
273 | 
274 | The React idioms that appear in that guide were produced by LLMs, not by me, and I can't fully explain how they work, but I am now confident it will be straightforward for React-savvy developers to extend XMLUI. What's more, I can now see the boundary between component builders and solution builders begin to blur. I am mainly a solution builder who has always depended on component builders to accomplish anything useful at that level. The fact that I was able to accomplish this useful thing myself feels significant.
275 | 
276 | ## Deployment
277 | 
278 | Here's the minimal XMLUI deployment footprint for the `TableEditor`.
279 | 
280 | ```
281 | TableEditor
282 | ├── Main.xmlui
283 | ├── index.html
284 | └── xmlui
285 |     └── 0.9.67.js
286 | ```
287 | 
288 | The `index.html` just sources the latest [standalone build](https://docs.xmlui.org/change-log) of XMLUI.
289 | 
290 | ```html
291 | <script src="xmlui/0.9.67.js"></script>
292 | ```
293 | 
294 | Here's `Main.xmlui`.
295 | 
296 | ```xml
297 | <App var.markdown="">
298 |   <Card>
299 |     <TableEditor
300 |       id="tableEditor"
301 |       size="xs"
302 |       onDidChange="{(e) => { markdown = e.markdown }}"
303 |     />
304 |   </Card>
305 | <Card>
306 |   <HStack>
307 |     <Text variant="codefence" preserveLinebreaks="{true}">
308 |       { markdown }
309 |     </Text>
310 |     <SpaceFiller />
311 |     <Button
312 |       icon="copy"
313 |       variant="ghost"
314 |       size="xs"
315 |       onClick="navigator.clipboard.writeText(markdown)"
316 |     />
317 |   </HStack>
318 | </Card>
319 | </App>
320 | ```
321 | 
322 | You can use any static webserver to host the app. You can even run it [from an AWS bucket](http://xmlui-table-editor.s3-website-us-east-1.amazonaws.com/).
323 | 
324 | For XMLUI Invoice we provide a [test server](https://github.com/xmlui-org/xmlui-invoice) that includes a localhost-only static server, embeds sqlite, and adds a CORS proxy for apps that need that support when talking to APIs (like Hubspot's) that require CORS. You may need to wrap similar capabilities around your XMLUI apps but the minimal deployment is dead simple.
325 | 
326 | ## Web development for the rest of us
327 | 
328 | XMLUI was conceived by Gent Hito who founded [/n software](https://www.nsoftware.com/) and [CData](https://cdata.com). The mission of /n software: make network communication easy for developers. For CData: make data access easy for developers. And now for XMLUI: make UI easy for developers.
329 | 
330 | "We are backend people," Gent says. "All our components are invisible, and when we tried to build simple business UIs we were surprised to find how hard and frustrating that was."
331 | 
332 | Those of us who remember the Visual Basic era know it wasn't always that way. But the web platform has never been friendly to solution builders who need to create user interfaces. That's become a game for specialists who can wrap their heads around an ongoing explosion of complexity.
333 | 
334 | It shouldn't be that way. Some apps do require special expertise. But many shouldn't. If you are /n software, and you need to give your customers an interface to monitor and control the [CoreSSH Server](https://www.nsoftware.com/coresshserver), you shouldn't need to hire React and CSS pros to make that happen. Your team should be able to do it for themselves and [now they can](https://demo.coressh.com/).
335 | 
336 | I'm having a blast creating interfaces that would otherwise be out of my reach. Will you have the same experience? Give it a try and [let us know](mailto:[email protected]) how it goes!
```
Page 99/181FirstPrevNextLast