#
tokens: 42644/50000 2/1633 files (page 107/190)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 107 of 190. Use http://codebase.md/xmlui-org/xmlui/%7Bimage%7D?lines=true&page={x} to view the full context.

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/docs/public/pages/build-hello-world-component.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Build a Hello World component
  2 | 
  3 | In this tutorial we'll build a HelloWorld component that demonstrates the core patterns for XMLUI component development.
  4 | 
  5 | The XMLUI framework supports two types of components:
  6 | 
  7 | **Core components** are built into the main XMLUI library and available by default. Components like Button, Text, Card, and Stack are always available in any XMLUI app.
  8 | 
  9 | **Extension packages** are standalone components that can be optionally included. They are built, distributed, and imported separately, making them perfect for custom components that aren't needed by every XMLUI application.
 10 | 
 11 | We'll build an extension package so the HelloWorld component can:
 12 | 
 13 | - Live separately from the core XMLUI library
 14 | - Be optionally included in standalone apps
 15 | - Be distributed and reused across different projects
 16 | 
 17 | Extensions are the recommended approach for custom components. By the end of this guide, you'll have created a HelloWorld component that:
 18 | 
 19 | - Displays a customizable greeting message
 20 | - Features an interactive click counter
 21 | - Uses XMLUI's standard theming system
 22 | - Defines event handlers
 23 | - Provides callable methods
 24 | 
 25 | ## XMLUI component architecture
 26 | 
 27 | XMLUI components are made of three main parts:
 28 | 
 29 | 1. Native React component (`HelloWorldNative.tsx`) - The actual React implementation
 30 | 2. Component metadata (`HelloWorld.tsx`) - Describes props and integrates with XMLUI
 31 | 3. For visual components, a .scss file (`HelloWorld.module.scss`)
 32 | 
 33 | This separation allows XMLUI to understand your component's interface while maintaining clean React code.
 34 | 
 35 | ## Prerequisites
 36 | 
 37 | - Familiarity with React and TypeScript
 38 | - Basic understanding of XMLUI markup
 39 | - Node.js 18.0.0 or higher
 40 | - npm (comes with Node.js)
 41 | 
 42 | ## Step 1: Create your project directory
 43 | 
 44 | Let's start by creating a new directory for your HelloWorld component project.
 45 | 
 46 | **Windows**
 47 | 
 48 | ```xmlui copy
 49 | mkdir xmlui-hello-world
 50 | cd xmlui-hello-world
 51 | ```
 52 | 
 53 | **Mac / WSL / Linux**
 54 | 
 55 | ```xmlui copy
 56 | mkdir xmlui-hello-world
 57 | cd xmlui-hello-world
 58 | ```
 59 | 
 60 | This creates a fresh project directory where you'll build your component from scratch.
 61 | 
 62 | > [!INFO]
 63 | > This page includes playground examples that use the HelloWorld component. They are available here because this site loads the final extension package that you will build. That means the live playground examples here reflect the final state, not the interim states described as we go along. But in the standalone app that you'll create you will see the progression exactly as described here.
 64 | 
 65 | ## Step 2: Create the package configuration
 66 | 
 67 | First, let's initialize a new npm project and install the xmlui package:
 68 | 
 69 | ```xmlui copy
 70 | npm init -y
 71 | npm install --save-dev xmlui
 72 | ```
 73 | 
 74 | This creates a basic `package.json` and installs the xmlui package as a development dependency.
 75 | 
 76 | Now let's update the `package.json` with the proper configuration for our extension:
 77 | 
 78 | ```xmlui copy
 79 | {
 80 |   "name": "xmlui-hello-world",
 81 |   "version": "0.1.0",
 82 |   "type": "module",
 83 |   "scripts": {
 84 |     "build:extension": "xmlui build-lib"
 85 |   },
 86 |   "devDependencies": {
 87 |     "xmlui": "*"
 88 |   },
 89 |   "main": "./dist/xmlui-hello-world.js",
 90 |   "module": "./dist/xmlui-hello-world.mjs",
 91 |   "exports": {
 92 |     ".": {
 93 |       "import": "./dist/xmlui-hello-world.mjs",
 94 |       "require": "./dist/xmlui-hello-world.js"
 95 |     }
 96 |   },
 97 |   "files": [
 98 |     "dist"
 99 |   ]
100 | }
101 | ```
102 | 
103 | The build system will generate both:
104 | 
105 | - xmlui-hello-world.js (CommonJS/UMD for browser script tags)
106 | - xmlui-hello-world.mjs (ES modules for import statements)
107 | 
108 | `xmlui-hello-world.js` is the file you'll pull into a standalone XMLUI app using a `<script>` tag.
109 | 
110 | ## Step 3: Create the React component
111 | 
112 | First, let's create the `src` directory for our component files:
113 | 
114 | ```xmlui copy
115 | mkdir src
116 | ```
117 | 
118 | Create `src/HelloWorldNative.tsx` with the core React implementation.
119 | 
120 | ```xmlui copy
121 | import React, { useState } from "react";
122 | import styles from "./HelloWorld.module.scss";
123 | 
124 | type Props = {
125 |   id?: string;
126 |   message?: string;
127 | };
128 | 
129 | export const defaultProps = {
130 |   message: "Hello, World!",
131 | };
132 | 
133 | export function HelloWorld({
134 |   id,
135 |   message = defaultProps.message,
136 | }: Props) {
137 |   const [clickCount, setClickCount] = useState(0);
138 | 
139 |   const handleClick = () => {
140 |     setClickCount(clickCount + 1);
141 |   };
142 | 
143 |   return (
144 |     <div className={styles.container} id={id}>
145 |       <h2 className={styles.message}>{message}</h2>
146 |       <button className={styles.button} onClick={handleClick}>
147 |         Click me!
148 |       </button>
149 |       <div className={styles.counter}>Clicks: {clickCount}</div>
150 |     </div>
151 |   );
152 | }
153 | ```
154 | 
155 | This creates the core React component with:
156 | 
157 | - Essential props (id, message)
158 | - Internal click counter
159 | 
160 | ## Step 4: Create basic styles
161 | 
162 | ```xmlui copy
163 | .container {
164 |   background-color: #f5f5f5;
165 |   color: #333;
166 |   padding: 1rem;
167 |   border-radius: 8px;
168 |   text-align: center;
169 |   display: inline-block;
170 |   min-width: 200px;
171 | }
172 | 
173 | .message {
174 |   margin: 0 0 1rem 0;
175 |   font-size: 1.5rem;
176 | }
177 | 
178 | .button {
179 |   background-color: #4a90e2;
180 |   color: white;
181 |   border: none;
182 |   padding: 0.75rem 1.5rem;
183 |   border-radius: 4px;
184 |   cursor: pointer;
185 |   font-size: 1rem;
186 |   margin-bottom: 1rem;
187 | 
188 |   &:hover {
189 |     opacity: 0.9;
190 |   }
191 | }
192 | 
193 | .counter {
194 |   font-size: 1.2rem;
195 |   font-weight: bold;
196 | }
197 | ```
198 | 
199 | This SCSS module defines the basic visual styling for our HelloWorld component:
200 | 
201 | - `.container` - Main wrapper with background, padding, and layout
202 | - `.message` - Styling for the greeting text
203 | - `.button` - Interactive button with hover effects
204 | - `.counter` - Display for the click count
205 | 
206 | At this stage, we use hardcoded colors. In Step 9, we'll replace these theme variables.
207 | 
208 | ## Step 5: Create component metadata and renderer
209 | 
210 | Create `HelloWorld.tsx`.
211 | 
212 | ```xmlui copy
213 | import styles from "./HelloWorld.module.scss";
214 | import { createComponentRenderer, createMetadata } from "xmlui";
215 | import { HelloWorld, defaultProps } from "./HelloWorldNative";
216 | 
217 | const HelloWorldMd = createMetadata({
218 |   description: "`HelloWorld` is a demonstration component.",
219 |   status: "experimental",
220 |   props: {
221 |     message: {
222 |       description: "The message to display.",
223 |       isRequired: false,
224 |       type: "string",
225 |       defaultValue: defaultProps.message,
226 |     },
227 |   },
228 | });
229 | 
230 | export const helloWorldComponentRenderer = createComponentRenderer(
231 |   "HelloWorld",
232 |   HelloWorldMd,
233 |   ({ node, extractValue }) => {
234 |     return (
235 |       <HelloWorld
236 |         id={extractValue.asOptionalString(node.props?.id)}
237 |         message={extractValue.asOptionalString(node.props?.message)}
238 |       />
239 |     );
240 |   }
241 | );
242 | ```
243 | 
244 | **What we're creating**
245 | 
246 | This file bridges the gap between XMLUI markup and React components.
247 | 
248 | - Metadata (`HelloWorldMd`) - Documents the component's props, behavior, and usage
249 | - Renderer (`helloWorldComponentRenderer`) - Converts XMLUI markup to React component calls
250 | 
251 | **The renderer pattern**
252 | 
253 | The renderer function receives XMLUI context (node, extractValue, etc.) and returns a React component.
254 | 
255 | It:
256 | 
257 | - Extracts prop values from XMLUI markup using `extractValue.asOptionalString()`
258 | - Passes them to the native React component
259 | - Handles optional props gracefully (undefined becomes default values)
260 | 
261 | This pattern enables XMLUI to:
262 | 
263 | - Validate markup against metadata
264 | - Provide IntelliSense and documentation
265 | - Handle prop type conversion automatically
266 | - Support XMLUI-specific features like theming (step 9) and event handling (Step 10)
267 | 
268 | ## Step 6: Create the extension index
269 | 
270 | Create `src/index.tsx` which exports your component as an extension.
271 | 
272 | ```xmlui copy
273 | cat > src/index.tsx << 'EOF'
274 | import { helloWorldComponentRenderer } from "./HelloWorld";
275 | 
276 | export default {
277 |   namespace: "XMLUIExtensions",
278 |   components: [helloWorldComponentRenderer],
279 | };
280 | EOF
281 | ```
282 | 
283 | This creates the main entry point that exports your HelloWorld component under the XMLUIExtensions namespace.
284 | 
285 | ## Step 7: Build the extension
286 | 
287 | ```xmlui copy
288 | npm run build:extension
289 | ```
290 | 
291 | This creates `xmlui-hello-world.js` in the `dist` folder.
292 | 
293 | ```xmlui-pg noHeader
294 | ---app
295 | <TreeDisplay content="
296 | packages/xmlui-hello-world
297 |  package.json
298 |  src
299 |   index.tsx
300 |   HelloWorld.tsx
301 |   HelloWorldNative.tsx
302 |   HelloWorld.module.scss
303 |  dist
304 |   xmlui-hello-world.js
305 |  " />
306 | ```
307 | 
308 | ## Step 8: Test the extension
309 | 
310 | Since we've integrated it into the docs site, you can see it live right here.
311 | 
312 | ```xmlui-pg
313 | ---app display
314 | <App>
315 |   <VStack gap="2rem" padding="2rem">
316 |     <H1>HelloWorld Component Live Demo</H1>
317 | 
318 |     <Card>
319 |       <HelloWorld message="Hello from the docs site!" />
320 |     </Card>
321 | 
322 |   </VStack>
323 | 
324 |   <script>
325 |     // Event handlers for the HelloWorld component
326 |     window.addEventListener('helloWorldClick', (event) => {
327 |       console.log('HelloWorld clicked!', event.detail);
328 |     });
329 |   </script>
330 | </App>
331 | ```
332 | 
333 | But you will want to see it in a standalone app. Let's create a simple test app to verify our component works.
334 | 
335 | First, create a test directory and an `xmlui` subdirectory within it:
336 | 
337 | ```xmlui copy
338 | mkdir test-app
339 | cd test-app
340 | mkdir xmlui
341 | ```
342 | 
343 | Now, copy your built component into the `xmlui` subdirectory:
344 | 
345 | ```xmlui copy
346 | cp ../dist/xmlui-hello-world.js xmlui/xmlui-hello-world.js
347 | ```
348 | 
349 | Create the `Main.xmlui` file with your component's markup:
350 | 
351 | ```xmlui copy
352 | <App>
353 |   <VStack gap="2rem" padding="2rem">
354 |     <Heading>HelloWorld Component Test</Heading>
355 |     <HelloWorld message="Hello from standalone app!" />
356 |   </VStack>
357 | 
358 |   <script>
359 |     // Event handlers for the HelloWorld component
360 |     window.addEventListener('helloWorldClick', (event) => {
361 |       console.log('HelloWorld clicked!', event.detail);
362 |     });
363 |   </script>
364 | </App>
365 | ```
366 | 
367 | Finally, create a simple `index.html` file to load the XMLUI engine from CDN and your component:
368 | 
369 | ```xmlui copy
370 | <!DOCTYPE html>
371 | <html lang="en">
372 | <head>
373 |   <meta charset="UTF-8" />
374 |   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
375 |   <title>HelloWorld Extension Test</title>
376 |   <script src="https://unpkg.com/xmlui@latest/dist/standalone/xmlui-standalone.umd.js"></script>
377 |   <script src="xmlui/xmlui-hello-world.js"></script>
378 | </head>
379 | <body>
380 | </body>
381 | </html>
382 | ```
383 | 
384 | This creates a simple test app that loads your component.
385 | 
386 | To run the app with Python:
387 | 
388 | ```xmlui copy
389 | python -m http.server # visit 8000
390 | ```
391 | 
392 | With Node.js:
393 | 
394 | ```xmlui copy
395 | npx server # visit 3000
396 | ```
397 | 
398 | ## Step 9: Add theming support
399 | 
400 | So far, our HelloWorld component uses hardcoded colors. Let's integrate it with XMLUI's theming system to make it more flexible and consistent with the rest of the UI.
401 | 
402 | **Understanding XMLUI's theme system**
403 | 
404 | XMLUI provides a sophisticated theming system that:
405 | 
406 | - Uses semantic design tokens (like `$color-surface-50`, `$color-content-primary`)
407 | - Automatically supports light and dark modes
408 | - Maintains consistency across all components
409 | - Allows runtime customization via the `<Theme>` component
410 | 
411 | **Adding theme variables**
412 | 
413 | Let's update our SCSS to use XMLUI's theme system:
414 | 
415 | ```xmlui copy
416 | @use "xmlui/themes.scss" as t;
417 | 
418 | $themeVars: ();
419 | @function createThemeVar($componentVariable) {
420 |   $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
421 |   @return t.getThemeVar($themeVars, $componentVariable);
422 | }
423 | 
424 | $component: "HelloWorld";
425 | 
426 | // Define theme variables for our component
427 | $backgroundColor: createThemeVar("backgroundColor-#{$component}");
428 | $textColor: createThemeVar("textColor-#{$component}");
429 | 
430 | .container {
431 |   background-color: $backgroundColor;
432 |   color: $textColor;
433 |   padding: 1rem;
434 |   border-radius: 8px;
435 |   text-align: center;
436 |   display: inline-block;
437 |   min-width: 200px;
438 | }
439 | 
440 | .message {
441 |   margin: 0 0 1rem 0;
442 |   font-size: 1.5rem;
443 | }
444 | 
445 | .button {
446 |   background-color: #4a90e2;
447 |   color: white;
448 |   border: none;
449 |   padding: 0.75rem 1.5rem;
450 |   border-radius: 4px;
451 |   cursor: pointer;
452 |   font-size: 1rem;
453 |   margin-bottom: 1rem;
454 | 
455 |   &:hover {
456 |     opacity: 0.9;
457 |   }
458 | }
459 | 
460 | .counter {
461 |   font-size: 1.2rem;
462 |   font-weight: bold;
463 | }
464 | 
465 | :export {
466 |   themeVars: t.json-stringify($themeVars);
467 | }
468 | ```
469 | 
470 | **What changed**
471 | 
472 | Instead of hardcoded colors like `#f5f5f5` and `#333`, we now use:
473 | 
474 | - `$backgroundColor` - Uses XMLUI's surface color tokens
475 | - `$textColor` - Uses XMLUI's content color tokens
476 | 
477 | The `createThemeVar()` function registers these variables with XMLUI, making them available for customization via the `<Theme>` component and automatic light/dark mode adaptation.
478 | 
479 | The `:export { themeVars: t.json-stringify($themeVars); }` exports the theme variables so XMLUI can read them.
480 | 
481 | **Update component metadata**
482 | 
483 | We also need to tell XMLUI about our theme variables. Update the metadata in `HelloWorld.tsx`:
484 | 
485 | ```xmlui copy
486 | import styles from "./HelloWorld.module.scss";
487 | import { createComponentRenderer, parseScssVar, createMetadata } from "xmlui";
488 | import { HelloWorld, defaultProps } from "./HelloWorldNative";
489 | 
490 | const HelloWorldMd = createMetadata({
491 |   description: "`HelloWorld` is a demonstration component.",
492 |   status: "experimental",
493 |   props: {
494 |     message: {
495 |       description: "The message to display.",
496 |       isRequired: false,
497 |       type: "string",
498 |       defaultValue: defaultProps.message,
499 |     },
500 |   },
501 |   themeVars: parseScssVar(styles.themeVars),
502 |   defaultThemeVars: {
503 |     [`backgroundColor-HelloWorld`]: "$color-surface-50",
504 |     [`textColor-HelloWorld`]: "$color-content-primary",
505 |     dark: {
506 |       [`backgroundColor-HelloWorld`]: "$color-surface-800",
507 |       // No textColor override needed - $color-content-primary should auto-adapt
508 |     },
509 |   },
510 | });
511 | 
512 | export const helloWorldComponentRenderer = createComponentRenderer(
513 |   "HelloWorld",
514 |   HelloWorldMd,
515 |   ({ node, extractValue }) => {
516 |     return (
517 |       <HelloWorld
518 |         id={extractValue.asOptionalString(node.props?.id)}
519 |         message={extractValue.asOptionalString(node.props?.message)}
520 |       />
521 |     );
522 |   }
523 | );
524 | ```
525 | 
526 | **Rebuild and test**
527 | 
528 | ```xmlui copy
529 | npm run build:extension
530 | ```
531 | 
532 | Now your component uses XMLUI's theme system! It will automatically adapt to light/dark modes and can be customized using the `<Theme>` component.
533 | 
534 | **Test the themed component**
535 | 
536 | Copy the new `xmlui-hello-world.js` into your standalone app's `xmlui` folder, and update its `Main.xmlui`.
537 | 
538 | ```xmlui-pg
539 | ---app display copy
540 | <App>
541 |   <VStack gap="2rem" padding="2rem">
542 |     <H1>HelloWorld with Theme Variables</H1>
543 | 
544 |     <HelloWorld message="Default styling" />
545 | 
546 |     <Card>
547 |       <H2>Custom Colors</H2>
548 |       <Theme
549 |         backgroundColor-HelloWorld="$color-warn-300"
550 |         textColor-HelloWorld="$textColor-primary"
551 |       >
552 |         <HelloWorld message="Custom colors!" />
553 |       </Theme>
554 |     </Card>
555 | 
556 |     <ToneSwitch />
557 |   </VStack>
558 | </App>
559 | ```
560 | 
561 | Notice how the component now uses theme variables instead of hardcoded colors. The `<Theme>` component allows you to override any theme variable at runtime, making your components incredibly flexible for different contexts and user preferences.
562 | 
563 | ## Step 10: Add event handling
564 | 
565 | The HelloWorld component has a click handler that increments a counter, and a reset that sets the count to zero. Let's add event definitions to signal parent components when these events happen.
566 | 
567 | **Add event definitions**
568 | 
569 | Update the component metadata in `src/HelloWorld.tsx`:
570 | 
571 | ```xmlui copy
572 | import styles from "./HelloWorld.module.scss";
573 | import { createComponentRenderer, parseScssVar, createMetadata } from "xmlui";
574 | import { HelloWorld, defaultProps } from "./HelloWorldNative";
575 | 
576 | const HelloWorldMd = createMetadata({
577 |   description:  "`HelloWorld` is a demonstration component.",
578 |   status: "experimental",
579 |   props: {
580 |     message: {
581 |       description: "The message to display.",
582 |       isRequired: false,
583 |       type: "string",
584 |       defaultValue: defaultProps.message,
585 |     },
586 |   },
587 |   events: {
588 |     onClick: {
589 |       description:
590 |         "Triggered when the click button is pressed. " + "Receives the current click count.",
591 |       type: "function",
592 |     },
593 |     onReset: {
594 |       description:
595 |         "Triggered when the reset button is pressed. " + "Called when count is reset to 0.",
596 |       type: "function",
597 |     },
598 |   },
599 |   themeVars: parseScssVar(styles.themeVars),
600 |   defaultThemeVars: {
601 |     [`backgroundColor-HelloWorld`]: "$color-surface-50",
602 |     [`textColor-HelloWorld`]: "$color-content-primary",
603 |     dark: {
604 |       [`backgroundColor-HelloWorld`]: "$color-surface-800",
605 |       // No textColor override needed - $color-content-primary should auto-adapt
606 |     },
607 |   },
608 | });
609 | 
610 | export const helloWorldComponentRenderer = createComponentRenderer(
611 |   "HelloWorld",
612 |   HelloWorldMd,
613 | 
614 |   ({ node, extractValue, lookupEventHandler, className }) => {
615 |     return (
616 |       <HelloWorld
617 |         id={extractValue.asOptionalString(node.props?.id)}
618 |         message={extractValue.asOptionalString(node.props?.message)}
619 |         onClick={lookupEventHandler("onClick")}
620 |         onReset={lookupEventHandler("onReset")}
621 |         className={className}
622 |       />
623 |     );
624 |   },
625 | );
626 | ```
627 | 
628 | **New props**
629 | 
630 | - `onClick?: (event: React.MouseEvent) => void` - Called when the click button is pressed
631 | - `onReset?: (event: React.MouseEvent) => void` - Called when the reset button is pressed
632 | 
633 | **Event handler changes:**
634 | 
635 | - `handleClick` now calls `onClick?.(event)` after updating internal state
636 | - `handleReset` now calls `onReset?.(event)` after resetting the counter
637 | - Both pass the DOM event object (not custom data) to match XMLUI's event system
638 | 
639 | **Update the native component**
640 | 
641 | Update `src/HelloWorldNative.tsx` to accept and call the event handler.
642 | 
643 | ```xmlui copy
644 | import React, { useState } from "react";
645 | import styles from "./HelloWorld.module.scss";
646 | 
647 | type Props = {
648 |   id?: string;
649 |   message?: string;
650 |   className?: string;
651 |   onClick?: (event: React.MouseEvent) => void;
652 |   onReset?: (event: React.MouseEvent) => void;
653 | };
654 | 
655 | export const defaultProps = {
656 |   message: "Hello, World!",
657 | };
658 | 
659 | export const HelloWorld = React.forwardRef<HTMLDivElement, Props>(
660 |   function HelloWorld(
661 |     {
662 |       id,
663 |       message = defaultProps.message,
664 |       className,
665 |       onClick,
666 |       onReset
667 |     },
668 |     ref
669 |   ) {
670 |     const [clickCount, setClickCount] = useState(0);
671 | 
672 |     const handleClick = (event: React.MouseEvent) => {
673 |       const newCount = clickCount + 1;
674 |       setClickCount(newCount);
675 |       onClick?.(event);
676 |     };
677 | 
678 |     const handleReset = (event: React.MouseEvent) => {
679 |       setClickCount(0);
680 |       onReset?.(event);
681 |     };
682 | 
683 |     return (
684 |       <div className={`${styles.container} ${className || ''}`} id={id}>
685 |         <h2 className={styles.message}>{message}</h2>
686 |         <button
687 |            className={styles.button}
688 |               onClick={handleClick}
689 |             >
690 |               Click me!
691 |             </button>
692 |             <div className={styles.counter}>
693 |               Clicks: <span className={styles.count}>{clickCount}</span>
694 |             </div>
695 | 
696 |             {clickCount > 0 && (
697 |               <button
698 |                 className={styles.button}
699 |                 onClick={handleReset}
700 |               >
701 |                 Reset
702 |               </button>
703 |             )}
704 |           </div>
705 |     );
706 |   }
707 | );
708 | ```
709 | 
710 | **Metadata changes:**
711 | 
712 | - Added `events` section defining `onClick` and `onReset` event handlers
713 | - Each event includes description and type information for documentation
714 | 
715 | **Renderer changes:**
716 | 
717 | - Added `lookupEventHandler` to the renderer context
718 | - `lookupEventHandler("onClick")` and `lookupEventHandler("onReset")` convert XMLUI event bindings to function references
719 | - These function references are passed to the native React component
720 | 
721 | **The event flow:**
722 | 
723 | 1. XMLUI markup: `<HelloWorld onClick="handleHelloClick" />`
724 | 2. Renderer: `lookupEventHandler("onClick")` finds the `handleHelloClick` function
725 | 3. Native component: Receives the function as `onClick` prop
726 | 4. User interaction: Triggers the function with the DOM event
727 | 
728 | **Rebuild the extension**
729 | 
730 | ```xmlui copy
731 | npm run build:extension
732 | ```
733 | 
734 | **Define the handlers**
735 | 
736 | This site's `index.html` defines these handler functions. For your standalone app you'll need to add them into its `index.html`.
737 | 
738 | ```xmlui copy
739 | <script>
740 | window.handleHelloClick = function(event) {
741 |   console.log('Hello World clicked!', event);
742 |   alert('Button clicked!');
743 | };
744 | 
745 | window.handleHelloReset = function(event) {
746 |   console.log('Hello World reset!', event);
747 |   alert('Counter was reset!');
748 | };
749 | </script>
750 | ```
751 | 
752 | **Test event handling**
753 | 
754 | Copy the new `xmlui-hello-world.js` into your standalone app's `xmlui` folder, and update its `Main.xmlui`.
755 | 
756 | Now you can use the component with event handling.
757 | 
758 | ```xmlui-pg
759 | ---app display copy
760 | <App>
761 |     <HelloWorld
762 |           onClick="handleHelloClick"
763 |           onReset="handleHelloReset"
764 |         />
765 | </App>
766 | ```
767 | 
768 | ## Step 11: Add component APIs (external methods)
769 | 
770 | Update `src/HelloWorldNative.tsx`.
771 | 
772 | ```xmlui copy
773 | import React, { useState, useEffect } from "react";
774 | import styles from "./HelloWorld.module.scss";
775 | import type { RegisterComponentApiFn } from "xmlui";
776 | 
777 | type Props = {
778 |   id?: string;
779 |   message?: string;
780 |   className?: string;
781 |   onClick?: (event: React.MouseEvent) => void;
782 |   onReset?: (event: React.MouseEvent) => void;
783 |   registerComponentApi?: RegisterComponentApiFn;
784 | };
785 | 
786 | export const defaultProps = {
787 |   message: "Hello, World!",
788 | };
789 | 
790 | export const HelloWorld = React.forwardRef<HTMLDivElement, Props>(
791 |   function HelloWorld(
792 |     {
793 |       id,
794 |       message = defaultProps.message,
795 |       className,
796 |       onClick,
797 |       onReset,
798 |       registerComponentApi
799 |     },
800 |     ref
801 |   ) {
802 |     const [clickCount, setClickCount] = useState(0);
803 | 
804 |     // Create setValue method for external API access
805 |     const setValue = (newCount: number) => {
806 |       setClickCount(newCount);
807 |     };
808 | 
809 |     // Register component API
810 |     useEffect(() => {
811 |       registerComponentApi?.({
812 |         setValue,
813 |         value: clickCount,
814 |       });
815 |     }, [registerComponentApi, setValue, clickCount]);
816 | 
817 |     const handleClick = (event: React.MouseEvent) => {
818 |       const newCount = clickCount + 1;
819 |       setClickCount(newCount);
820 |       onClick?.(event);
821 |     };
822 | 
823 |     const handleReset = (event: React.MouseEvent) => {
824 |       setClickCount(0);
825 |       onReset?.(event);
826 |     };
827 | 
828 |     return (
829 |       <div className={`${styles.container} ${className || ''}`} id={id} ref={ref}>
830 |         <h2 className={styles.message}>{message}</h2>
831 |         <button
832 |            className={styles.button}
833 |               onClick={handleClick}
834 |             >
835 |               Click me!
836 |             </button>
837 |             <div className={styles.counter}>
838 |               Clicks: <span className={styles.count}>{clickCount}</span>
839 |             </div>
840 | 
841 |             {clickCount > 0 && (
842 |               <button
843 |                 className={styles.button}
844 |                 onClick={handleReset}
845 |               >
846 |                 Reset
847 |               </button>
848 |             )}
849 |           </div>
850 |     );
851 |   }
852 | );
853 | ```
854 | 
855 | **New props**
856 | 
857 | - `registerComponentApi?: RegisterComponentApiFn` - Function to register component APIs with XMLUI
858 | 
859 | **New imports:**
860 | 
861 | - `useEffect` from React - For API registration and state synchronization
862 | - `RegisterComponentApiFn` type from "xmlui" - Type for the API registration function
863 | 
864 | **API registration:**
865 | 
866 | - `setValue` method - Allows external code to set the click count
867 | - `useEffect` hook registers the API with XMLUI, exposing both `setValue` and `value`
868 | - API updates whenever `clickCount` changes, ensuring `value` is always current
869 | 
870 | This enables XMLUI markup to directly call `demo.setValue(5)` and read `demo.value`.
871 | 
872 | Update `src/HelloWorldNative.tsx`.
873 | 
874 | ```xmlui copy
875 | cat > src/HelloWorld.tsx << 'EOF'
876 | import styles from "./HelloWorld.module.scss";
877 | import { createComponentRenderer, parseScssVar, createMetadata } from "xmlui";
878 | import { HelloWorld, defaultProps } from "./HelloWorldNative";
879 | 
880 | const HelloWorldMd = createMetadata({
881 |   description:
882 |     "`HelloWorld` is a demonstration component that shows basic XMLUI patterns.",
883 |   status: "experimental",
884 |   props: {
885 |     message: {
886 |       description: "The greeting message to display.",
887 |       isRequired: false,
888 |       type: "string",
889 |       defaultValue: defaultProps.message,
890 |     },
891 |   },
892 |   events: {
893 |     onClick: {
894 |       description:
895 |         "Triggered when the click button is pressed. " + "Receives the current click count.",
896 |       type: "function",
897 |     },
898 |     onReset: {
899 |       description:
900 |         "Triggered when the reset button is pressed. " + "Called when count is reset to 0.",
901 |       type: "function",
902 |     },
903 |   },
904 |   apis: {
905 |     value: {
906 |       description: "The current click count value.",
907 |       type: "number",
908 |     },
909 |     setValue: {
910 |       description: "Set the click count to a specific value.",
911 |       type: "function",
912 |     },
913 |   },
914 |   themeVars: parseScssVar(styles.themeVars),
915 |   defaultThemeVars: {
916 |     [`backgroundColor-HelloWorld`]: "$color-surface-50",
917 |     [`textColor-HelloWorld`]: "$color-content-primary",
918 |     dark: {
919 |       [`backgroundColor-HelloWorld`]: "$color-surface-800",
920 |       // No textColor override needed - $color-content-primary should auto-adapt
921 |     },
922 |   },
923 | });
924 | 
925 | export const helloWorldComponentRenderer = createComponentRenderer(
926 |   "HelloWorld",
927 |   HelloWorldMd,
928 | 
929 |   ({ node, extractValue, lookupEventHandler, className, registerComponentApi }) => {
930 |     return (
931 |       <HelloWorld
932 |         id={extractValue.asOptionalString(node.props?.id)}
933 |         message={extractValue.asOptionalString(node.props?.message)}
934 |         onClick={lookupEventHandler("onClick")}
935 |         onReset={lookupEventHandler("onReset")}
936 |         className={className}
937 |         registerComponentApi={registerComponentApi}
938 |       />
939 |     );
940 |   },
941 | );
942 | EOF
943 | ```
944 | 
945 | **Metadata**
946 | 
947 | - Added `apis` section defining `value` (number) and `setValue` (function) APIs
948 | 
949 | **Renderer Changes**
950 | 
951 | - Added `registerComponentApi` to the renderer context
952 | - Passes `registerComponentApi` to the native component for API registration
953 | 
954 | **The API flow:**
955 | 
956 | 1. XMLUI markup: `<HelloWorld id="demo" />` creates component with ID
957 | 2. Renderer: Registers component APIs via `registerComponentApi`
958 | 3. External access: `demo.setValue(5)` calls the component's setValue method
959 | 4. State reading: `demo.value` returns the current click count
960 | 
961 | ```xmlui copy
962 | npm run build:extension
963 | ```
964 | 
965 | Copy the new `xmlui-hello-world.js` into your standalone app's `xmlui` folder, and update its `Main.xmlui` to see this final version.
966 | 
967 | ```xmlui-pg
968 | ---app display copy
969 | <App xmlns:Extensions="component-ns:XMLUIExtensions">
970 | 
971 |     <Extensions:HelloWorld id="demo" message="API Demo" />
972 | 
973 |     <CHStack>
974 |       <Button onClick="{ console.log('demo.value', demo.value) }">Get Count</Button>
975 |       <Button onClick="{ demo.setValue(5) }">Set to 5</Button>
976 |       <Button onClick="{ demo.setValue(0) }">Reset</Button>
977 |     </CHStack>
978 | 
979 | </App>
980 | ```
981 | 
```

--------------------------------------------------------------------------------
/xmlui/src/components/App/App.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import styles from "./App.module.scss";
  2 | import drawerStyles from "./Sheet.module.scss";
  3 | 
  4 | import { type ComponentDef } from "../../abstractions/ComponentDefs";
  5 | import { createComponentRenderer } from "../../components-core/renderers";
  6 | import { parseScssVar } from "../../components-core/theming/themeVars";
  7 | 
  8 | import { createMetadata, dComponent } from "../../components/metadata-helpers";
  9 | import { appLayoutMd } from "./AppLayoutContext";
 10 | import { App, defaultProps } from "./AppNative";
 11 | import type { CSSProperties } from "react";
 12 | import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from "react";
 13 | import type { PageMd } from "../Pages/Pages";
 14 | import type { RenderChildFn } from "../../abstractions/RendererDefs";
 15 | import { IndexerContext } from "./IndexerContext";
 16 | import { createPortal } from "react-dom";
 17 | import { useAppContext } from "../../components-core/AppContext";
 18 | import { useSearchContextSetIndexing, useSearchContextUpdater } from "./SearchContext";
 19 | 
 20 | // --- Define a structure to represent navigation hierarchy
 21 | interface NavHierarchyNode {
 22 |   type: string;
 23 |   label: string;
 24 |   path?: string;
 25 |   children?: NavHierarchyNode[];
 26 | }
 27 | 
 28 | const COMP = "App";
 29 | 
 30 | export const AppMd = createMetadata({
 31 |   status: "stable",
 32 |   description:
 33 |     "The `App` component is the root container that defines your application's overall " +
 34 |     "structure and layout. It provides a complete UI framework with built-in navigation, " +
 35 |     "header, footer, and content areas that work together seamlessly.",
 36 |   props: {
 37 |     layout: {
 38 |       description:
 39 |         `This property sets the layout template of the app. This setting determines the position ` +
 40 |         `and size of the app parts (such as header, navigation bar, footer, etc.) and the app's ` +
 41 |         `scroll behavior.`,
 42 |       availableValues: appLayoutMd,
 43 |     },
 44 |     loggedInUser: {
 45 |       description:
 46 |         "Stores information about the currently logged-in user. By not defining this property, " +
 47 |         "you can indicate that no user is logged in.",
 48 |       valueType: "string",
 49 |     },
 50 |     logoTemplate: dComponent("Optional template of the app logo"),
 51 |     logo: {
 52 |       description: "Optional logo path",
 53 |       valueType: "string",
 54 |     },
 55 |     "logo-dark": {
 56 |       description: "Optional logo path in dark tone",
 57 |       valueType: "string",
 58 |     },
 59 |     "logo-light": {
 60 |       description: "Optional logo path in light tone",
 61 |       valueType: "string",
 62 |     },
 63 |     name: {
 64 |       description:
 65 |         "Optional application name (visible in the browser tab). When you do not define " +
 66 |         "this property, the tab name falls back to the one defined in the app\'s configuration. " +
 67 |         'If the name is not configured, "XMLUI App" is displayed in the tab.',
 68 |       valueType: "string",
 69 |     },
 70 |     scrollWholePage: {
 71 |       description:
 72 |         `This boolean property specifies whether the whole page should scroll (\`true\`) or just ` +
 73 |         `the content area (\`false\`). The default value is \`true\`.`,
 74 |       valueType: "boolean",
 75 |       defaultValue: defaultProps.scrollWholePage,
 76 |     },
 77 |     noScrollbarGutters: {
 78 |       description:
 79 |         "This boolean property specifies whether the scrollbar gutters should be hidden.",
 80 |       valueType: "boolean",
 81 |       defaultValue: defaultProps.noScrollbarGutters,
 82 |     },
 83 |     defaultTone: {
 84 |       description: 'This property sets the app\'s default tone ("light" or "dark").',
 85 |       valueType: "string",
 86 |       defaultValue: defaultProps.defaultTone,
 87 |       availableValues: ["light", "dark"],
 88 |     },
 89 |     defaultTheme: {
 90 |       description: "This property sets the app's default theme.",
 91 |       valueType: "string",
 92 |       defaultValue: defaultProps.defaultTheme,
 93 |     },
 94 |     autoDetectTone: {
 95 |       description: 
 96 |         'This boolean property enables automatic detection of the system theme preference. ' +
 97 |         'When set to true and no defaultTone is specified, the app will automatically use ' +
 98 |         '"light" or "dark" tone based on the user\'s system theme setting. The app will ' +
 99 |         'also respond to changes in the system theme preference.',
100 |       valueType: "boolean",
101 |       defaultValue: defaultProps.autoDetectTone,
102 |     },
103 |   },
104 |   events: {
105 |     ready: {
106 |       description: `This event fires when the \`${COMP}\` component finishes rendering on the page.`,
107 |     },
108 |     messageReceived: {
109 |       description: `This event fires when the \`${COMP}\` component receives a message from another window or iframe via the window.postMessage API.`,
110 |     },
111 |   },
112 |   themeVars: { ...parseScssVar(styles.themeVars), ...parseScssVar(drawerStyles.themeVars) },
113 |   limitThemeVarsToComponent: true,
114 |   themeVarDescriptions: {
115 |     "maxWidth-content-App":
116 |       "This theme variable defines the maximum width of the main content. If the main " +
117 |       "content is broader, the engine adds margins to keep the expected maximum size.",
118 |     "boxShadow‑header‑App": "This theme variable sets the shadow of the app's header section.",
119 |     "boxShadow‑navPanel‑App":
120 |       "This theme variable sets the shadow of the app's navigation panel section " +
121 |       "(visible only in vertical layouts).",
122 |     "width‑navPanel‑App":
123 |       "This theme variable sets the width of the navigation panel when the app is displayed " +
124 |       "with one of the vertical layouts.",
125 |   },
126 |   defaultThemeVars: {
127 |     "maxWidth-Drawer": "100%",
128 |     [`width-navPanel-${COMP}`]: "$space-64",
129 |     [`backgroundColor-navPanel-${COMP}`]: "$backgroundColor",
130 |     [`maxWidth-content-${COMP}`]: "$maxWidth-content",
131 |     [`maxWidth-${COMP}`]: "$maxWidth-content",
132 |     [`boxShadow-header-${COMP}`]: "none",
133 |     [`boxShadow-navPanel-${COMP}`]: "none",
134 |     [`scroll-padding-block-Pages`]: "$space-4",
135 |     [`backgroundColor-content-App`]: "$backgroundColor",
136 |     light: {
137 |       // --- No light-specific theme vars
138 |     },
139 |     dark: {
140 |       // --- No dark-specific theme vars
141 |     },
142 |   },
143 | });
144 | 
145 | 
146 | function AppNode({ node, extractValue, renderChild, className, lookupEventHandler, registerComponentApi }) {
147 |   // --- Use ref to track if we've already processed the navigation to avoid duplicates in strict mode
148 |   const processedNavRef = useRef(false);
149 | 
150 |   // --- Memoize the layout type to avoid unnecessary re-extraction
151 |   const layoutType = useMemo(
152 |     () => extractValue(node.props.layout),
153 |     [node.props.layout, extractValue],
154 |   );
155 | 
156 |   // --- Memoize helper functions that are used in multiple places
157 | 
158 |   // --- Parse a string into hierarchy labels, handling escaped pipe characters
159 |   const parseHierarchyLabels = useMemo(() => {
160 |     // Cache to hold previously computed results
161 |     const cache = new Map<string, string[]>();
162 | 
163 |     return (labelText: string): string[] => {
164 |       // Return cached result if we've seen this input before
165 |       if (cache.has(labelText)) {
166 |         return cache.get(labelText)!;
167 |       }
168 | 
169 |       const result: string[] = [];
170 |       let currentLabel = "";
171 |       let escaped = false;
172 | 
173 |       for (let i = 0; i < labelText.length; i++) {
174 |         const char = labelText[i];
175 | 
176 |         if (escaped) {
177 |           // --- If this character was escaped, just add it literally
178 |           currentLabel += char;
179 |           escaped = false;
180 |         } else if (char === "\\") {
181 |           // --- Start of an escape sequence
182 |           escaped = true;
183 |         } else if (char === "|") {
184 |           // --- Unescaped pipe indicates hierarchy separator
185 |           result.push(currentLabel.trim());
186 |           currentLabel = "";
187 |         } else {
188 |           // --- Regular character
189 |           currentLabel += char;
190 |         }
191 |       }
192 | 
193 |       // --- Don't forget to add the last segment
194 |       if (currentLabel.length > 0) {
195 |         result.push(currentLabel.trim());
196 |       }
197 | 
198 |       // Cache the result
199 |       cache.set(labelText, result);
200 | 
201 |       return result;
202 |     };
203 |   }, []);
204 | 
205 |   // --- Helper function to check if a label exists in the navigation hierarchy
206 |   const labelExistsInHierarchy = useMemo(() => {
207 |     // Cache for previously checked labels within a hierarchy
208 |     const cache = new Map<string, boolean>();
209 | 
210 |     return (searchLabel: string, hierarchy: any[]): boolean => {
211 |       // Create a cache key (could be improved with a better serialization of hierarchy)
212 |       const cacheKey = searchLabel + "_" + hierarchy.length;
213 | 
214 |       if (cache.has(cacheKey)) {
215 |         return cache.get(cacheKey)!;
216 |       }
217 | 
218 |       const result = hierarchy.some((node) => {
219 |         if (node.label === searchLabel) {
220 |           return true;
221 |         }
222 | 
223 |         if (node.children && node.children.length > 0) {
224 |           return labelExistsInHierarchy(searchLabel, node.children);
225 |         }
226 | 
227 |         return false;
228 |       });
229 | 
230 |       cache.set(cacheKey, result);
231 |       return result;
232 |     };
233 |   }, []);
234 | 
235 |   // --- Helper function to find or create NavGroups in the hierarchy
236 |   const findOrCreateNavGroup = useMemo(() => {
237 |     return (navItems: any[], groupLabel: string): any => {
238 |       // --- Check if a NavGroup with this label already exists
239 |       const existingGroup = navItems.find(
240 |         (item) => item.type === "NavGroup" && item.props?.label === groupLabel,
241 |       );
242 | 
243 |       if (existingGroup) {
244 |         return existingGroup;
245 |       }
246 | 
247 |       // --- Create a new NavGroup and add it to the array
248 |       const newGroup = {
249 |         type: "NavGroup",
250 |         props: {
251 |           label: groupLabel,
252 |         },
253 |         children: [],
254 |       };
255 | 
256 |       navItems.push(newGroup);
257 |       return newGroup;
258 |     };
259 |   }, []);
260 | 
261 |   const { AppHeader, Footer, NavPanel, Pages, restChildren } = useMemo(() => {
262 |     let AppHeader: ComponentDef;
263 |     let Footer: ComponentDef;
264 |     let NavPanel: ComponentDef;
265 |     let Pages: ComponentDef;
266 |     const restChildren: any[] = [];
267 |     node.children?.forEach((rootChild) => {
268 |       let transformedChild = { ...rootChild };
269 |       if (rootChild.type === "Theme") {
270 |         transformedChild.children = rootChild.children?.filter((child) => {
271 |           if (child.type === "AppHeader") {
272 |             AppHeader = {
273 |               ...rootChild,
274 |               children: [child],
275 |             };
276 |             return false;
277 |           } else if (child.type === "Footer") {
278 |             Footer = {
279 |               ...rootChild,
280 |               children: [child],
281 |             };
282 |             return false;
283 |           } else if (child.type === "NavPanel") {
284 |             NavPanel = {
285 |               ...rootChild,
286 |               children: [child],
287 |             };
288 |             return false;
289 |           }
290 |           return true;
291 |         });
292 |         if (!transformedChild.children.length) {
293 |           transformedChild = null;
294 |         }
295 |       }
296 |       if (rootChild.type === "AppHeader") {
297 |         AppHeader = rootChild;
298 |       } else if (rootChild.type === "Footer") {
299 |         Footer = rootChild;
300 |       } else if (rootChild.type === "NavPanel") {
301 |         NavPanel = rootChild;
302 |       } else if (rootChild.type === "Pages") {
303 |         Pages = rootChild;
304 |         restChildren.push(transformedChild);
305 |       } else if (transformedChild !== null) {
306 |         restChildren.push(transformedChild);
307 |       }
308 |     });
309 | 
310 |     // --- Check if there is any extra NavPanel in Pages
311 |     const extraNavs = extractNavPanelFromPages(
312 |       Pages,
313 |       NavPanel,
314 |       processedNavRef,
315 |       extractValue,
316 |       parseHierarchyLabels,
317 |       labelExistsInHierarchy,
318 |       findOrCreateNavGroup,
319 |     );
320 | 
321 |     // --- If we found extra navigation items
322 |     if (extraNavs?.length) {
323 |       if (NavPanel) {
324 |         // --- Create a new NavPanel with combined children instead of mutating the existing one
325 |         NavPanel = {
326 |           ...NavPanel,
327 |           children: NavPanel.children ? [...NavPanel.children, ...extraNavs] : extraNavs,
328 |         };
329 |       } else {
330 |         // --- Create a new NavPanel component definition if none exists
331 |         NavPanel = {
332 |           type: "NavPanel",
333 |           props: {},
334 |           children: extraNavs,
335 |         };
336 |       }
337 |     }
338 | 
339 |     return {
340 |       AppHeader,
341 |       Footer,
342 |       NavPanel,
343 |       Pages,
344 |       restChildren,
345 |     };
346 |   }, [
347 |     node.children,
348 |     extractValue,
349 |     parseHierarchyLabels,
350 |     labelExistsInHierarchy,
351 |     findOrCreateNavGroup,
352 |   ]);
353 | 
354 |   const applyDefaultContentPadding= !Pages;
355 | 
356 |   // Extract sticky property from Footer component
357 |   const footerSticky = useMemo(() => {
358 |     if (!Footer) return true;
359 |     
360 |     // Check if Footer is wrapped in Theme
361 |     let footerNode = Footer;
362 |     if (Footer.type === "Theme" && Footer.children?.length > 0) {
363 |       footerNode = Footer.children.find((child) => child.type === "Footer");
364 |     }
365 |     
366 |     if (footerNode?.type === "Footer" && footerNode.props?.sticky !== undefined) {
367 |       return extractValue.asOptionalBoolean(footerNode.props.sticky, true);
368 |     }
369 |     
370 |     return true;
371 |   }, [Footer, extractValue]);
372 | 
373 |   // --- Memoize all app props to prevent unnecessary re-renders
374 |   const appProps = useMemo(
375 |     () => ({
376 |       scrollWholePage: extractValue.asOptionalBoolean(node.props.scrollWholePage, true),
377 |       noScrollbarGutters: extractValue.asOptionalBoolean(node.props.noScrollbarGutters, false),
378 |       className,
379 |       layout: layoutType,
380 |       loggedInUser: extractValue(node.props.loggedInUser),
381 |       onReady: lookupEventHandler("ready"),
382 |       onMessageReceived: lookupEventHandler("messageReceived"),
383 |       name: extractValue(node.props.name),
384 |       logo: extractValue(node.props.logo),
385 |       logoDark: extractValue(node.props["logo-dark"]),
386 |       logoLight: extractValue(node.props["logo-light"]),
387 |       defaultTone: extractValue(node.props.defaultTone),
388 |       defaultTheme: extractValue(node.props.defaultTheme),
389 |       autoDetectTone: extractValue.asOptionalBoolean(node.props.autoDetectTone, false),
390 |       applyDefaultContentPadding,
391 |       footerSticky,
392 |     }),
393 |     [
394 |       extractValue,
395 |       layoutType,
396 |       lookupEventHandler,
397 |       node.props.loggedInUser,
398 |       node.props.noScrollbarGutters,
399 |       node.props.scrollWholePage,
400 |       node.props.name,
401 |       node.props.logo,
402 |       node.props["logo-dark"],
403 |       node.props["logo-light"],
404 |       node.props.defaultTone,
405 |       node.props.defaultTheme,
406 |       node.props.autoDetectTone,
407 |       className,
408 |       applyDefaultContentPadding,
409 |       footerSticky,
410 |     ],
411 |   );
412 | 
413 |   // Memoize the rendered children to prevent unnecessary re-renders
414 |   const renderedHeader = useMemo(() => renderChild(AppHeader), [AppHeader, renderChild]);
415 |   const renderedFooter = useMemo(() => renderChild(Footer), [Footer, renderChild]);
416 |   const renderedNavPanel = useMemo(() => renderChild(NavPanel), [NavPanel, renderChild]);
417 |   const renderedContent = useMemo(() => renderChild(restChildren), [restChildren, renderChild]);
418 | 
419 |   return (
420 |     <App
421 |       {...appProps}
422 |       header={renderedHeader}
423 |       footer={renderedFooter}
424 |       footerSticky={footerSticky}
425 |       navPanel={renderedNavPanel}
426 |       navPanelDef={NavPanel}
427 |       logoContentDef={node.props.logoTemplate}
428 |       renderChild={renderChild}
429 |       registerComponentApi={registerComponentApi}
430 |     >
431 |       {renderedContent}
432 |       <SearchIndexCollector Pages={Pages} renderChild={renderChild} />
433 |     </App>
434 |   );
435 | }
436 | 
437 | const HIDDEN_STYLE: CSSProperties = {
438 |   position: "absolute",
439 |   top: "-9999px",
440 |   display: "none",
441 | };
442 | 
443 | const indexerContextValue = {
444 |   indexing: true,
445 | };
446 | 
447 | function SearchIndexCollector({ Pages, renderChild }) {
448 |   const appContext = useAppContext();
449 |   const setIndexing = useSearchContextSetIndexing();
450 | 
451 |   const [isClient, setIsClient] = useState(false);
452 |   useEffect(() => {
453 |     setIsClient(true); // Ensure document.body is available
454 | 
455 |     return () => {
456 |       setIndexing(false);
457 |     };
458 |   }, [setIndexing]);
459 | 
460 |   // 1. Memoize the list of pages to be indexed
461 |   const pagesToIndex = useMemo(() => {
462 |     return (
463 |       Pages?.children?.filter(
464 |         (child) =>
465 |           child.type === "Page" && // Ensure 'Page' matches your actual component type name
466 |           child.props?.url && // Ensure URL exists
467 |           !child.props.url.includes("*") &&
468 |           !child.props.url.includes(":"),
469 |       ) || []
470 |     );
471 |   }, [Pages?.children]);
472 | 
473 |   const [currentIndex, setCurrentIndex] = useState(0);
474 |   const [isDoneIndexing, setIsDoneIndexing] = useState(false);
475 |   const [, startTransitionParent] = useTransition(); // Transition for parent updates
476 | 
477 |   const handlePageIndexed = useCallback(() => {
478 |     startTransitionParent(() => {
479 |       // Transition the update to the next page
480 |       setCurrentIndex((prevIndex) => {
481 |         const nextIndex = prevIndex + 1;
482 |         if (nextIndex >= pagesToIndex.length) {
483 |           // console.log("All pages indexed.");
484 |           setIsDoneIndexing(true); // All pages processed
485 |         }
486 |         return nextIndex;
487 |       });
488 |     });
489 |   }, [pagesToIndex.length]); // Recreate if the total number of pages changes
490 | 
491 |   if (!appContext.appGlobals?.searchIndexEnabled || isDoneIndexing || !isClient) {
492 |     return null;
493 |   }
494 | 
495 |   const currentPageToProcess = pagesToIndex[currentIndex];
496 | 
497 |   if (!currentPageToProcess) {
498 |     // This can happen if pagesToIndex is empty or currentIndex went out of bounds unexpectedly.
499 |     // Setting isDoneIndexing if pagesToIndex is empty initially.
500 |     if (pagesToIndex.length === 0 && currentIndex === 0 && !isDoneIndexing) {
501 |       setIsDoneIndexing(true);
502 |     }
503 |     return null;
504 |   }
505 | 
506 |   return (
507 |     <IndexerContext.Provider value={indexerContextValue}>
508 |       {createPortal(
509 |         <div style={HIDDEN_STYLE} aria-hidden="true">
510 |           {/* Render only one PageIndexer at a time */}
511 |           <PageIndexer
512 |             Page={currentPageToProcess}
513 |             renderChild={renderChild}
514 |             onIndexed={handlePageIndexed}
515 |             key={currentPageToProcess.props?.url || currentIndex} // Key ensures re-mount
516 |           />
517 |         </div>,
518 |         document.body,
519 |       )}
520 |     </IndexerContext.Provider>
521 |   );
522 | }
523 | 
524 | function PageIndexer({
525 |   Page,
526 |   renderChild,
527 |   onIndexed,
528 | }: {
529 |   Page: ComponentDef<typeof PageMd>; // Use the defined PageMdProps
530 |   renderChild: RenderChildFn;
531 |   onIndexed: () => void;
532 | }) {
533 |   const contentRef = useRef<HTMLDivElement>(null);
534 |   const pageUrl = Page.props?.url || "";
535 |   const navLabel = Page.props?.navLabel || "";
536 |   const searchContextUpdater = useSearchContextUpdater();
537 | 
538 |   const [isContentRendered, setIsContentRendered] = useState(false);
539 |   const [isCollected, setIsCollected] = useState(false);
540 |   const [isProcessing, startTransition] = useTransition();
541 | 
542 |   // Effect 1: Schedule the rendering of the Page's children (low priority)
543 |   useEffect(() => {
544 |     // console.log(`PageIndexer (${pageUrl}): Scheduling content render.`);
545 |     startTransition(() => {
546 |       setIsContentRendered(true); // This will trigger rendering of Page.children
547 |     });
548 |   }, [pageUrl]); // Re-run if the Page prop itself changes identity (due to key in parent)
549 | 
550 |   // Effect 2: Extract content once Page.children is rendered and ref is available (low priority)
551 |   useEffect(() => {
552 |     if (isContentRendered && contentRef.current && !isCollected && !isProcessing) {
553 |       // console.log(`PageIndexer (${pageUrl}): Content rendered, scheduling extraction.`);
554 |       startTransition(() => {
555 |         // console.log(`PageIndexer (${pageUrl}): Starting extraction...`);
556 |         const currentContent = contentRef.current; // Capture ref value
557 |         if (!currentContent) return;
558 | 
559 |         const clone = currentContent.cloneNode(true) as HTMLDivElement;
560 |         const elementsToRemove = clone.querySelectorAll("style, script");
561 |         elementsToRemove.forEach((el) => el.remove());
562 |         const titleElement = clone.querySelector("h1");
563 |         const title = titleElement
564 |           ? titleElement.innerText
565 |           : navLabel || pageUrl.split("/").pop() || pageUrl;
566 |         titleElement?.remove(); // Remove title element from clone to avoid duplication
567 |         const textContent = (clone.textContent || "").trim().replace(/\s+/g, " ");
568 | 
569 |         const entry = {
570 |           title: title,
571 |           content: textContent,
572 |           path: pageUrl,
573 |         };
574 | 
575 |         searchContextUpdater(entry);
576 |         // console.log(`PageIndexer (${pageUrl}): Extraction complete, signaling parent.`);
577 |         onIndexed(); // Signal completion to parent
578 |         setIsCollected(true); // Mark as collected
579 |       });
580 |     }
581 |   }, [
582 |     isContentRendered,
583 |     pageUrl,
584 |     searchContextUpdater,
585 |     onIndexed,
586 |     isCollected,
587 |     isProcessing,
588 |     navLabel,
589 |   ]); // Ensure all dependencies are listed
590 | 
591 |   // If this PageIndexer instance's work is done, or content not yet rendered, render nothing.
592 |   // The parent (SearchIndexCollector) will unmount this and mount the next one.
593 |   if (isCollected || !isContentRendered) {
594 |     // console.log(`PageIndexer (${pageUrl}): Null render (isCollected: ${isCollected}, isContentRendered: ${isContentRendered})`);
595 |     return null;
596 |   }
597 | 
598 |   // This part renders when isContentRendered is true and isCollected is false.
599 |   // The content needs to be in the DOM for contentRef.current to be populated.
600 |   // console.log(`PageIndexer (${pageUrl}): Rendering content for ref population.`);
601 |   return <div ref={contentRef}>{renderChild(Page.children)}</div>;
602 | }
603 | 
604 | export const appRenderer = createComponentRenderer(
605 |   COMP,
606 |   AppMd,
607 |   ({ node, extractValue, renderChild, className, lookupEventHandler, registerComponentApi }) => {
608 |     return (
609 |       <AppNode
610 |         node={node}
611 |         renderChild={renderChild}
612 |         extractValue={extractValue}
613 |         className={className}
614 |         lookupEventHandler={lookupEventHandler}
615 |         registerComponentApi={registerComponentApi}
616 |       />
617 |     );
618 |   },
619 | );
620 | 
621 | // --- Process the entire navigation tree recursively and build hierarchy
622 | function processNavItems(
623 |   items: ComponentDef[],
624 |   parentHierarchy: NavHierarchyNode[],
625 |   extractValue: (value: any) => any,
626 | ) {
627 |   items.forEach((navItem) => {
628 |     // --- Process NavLink items
629 |     if (navItem.type === "NavLink") {
630 |       let itemLabel = navItem.props?.label;
631 |       let itemPath = navItem.props?.to;
632 | 
633 |       if (!itemLabel) {
634 |         if (navItem.children?.length === 1 && navItem.children[0].type === "TextNode") {
635 |           itemLabel = navItem.children[0].props.value;
636 |         }
637 |       }
638 | 
639 |       if (itemLabel) {
640 |         const labelValue = extractValue(itemLabel);
641 | 
642 |         // --- Add to hierarchy
643 |         parentHierarchy.push({
644 |           type: "NavLink",
645 |           label: labelValue,
646 |           path: itemPath ? extractValue(itemPath) : undefined,
647 |         });
648 |       }
649 |     }
650 |     // --- Process NavGroup items (which may contain nested NavLink or NavGroup items)
651 |     else if (navItem.type === "NavGroup") {
652 |       let groupLabel = navItem.props?.label;
653 | 
654 |       if (groupLabel) {
655 |         const labelValue = extractValue(groupLabel);
656 | 
657 |         // --- Create group node
658 |         const groupNode: NavHierarchyNode = {
659 |           type: "NavGroup",
660 |           label: labelValue,
661 |           children: [],
662 |         };
663 | 
664 |         // --- Add to parent hierarchy
665 |         parentHierarchy.push(groupNode);
666 | 
667 |         // --- Recursively process children of the NavGroup
668 |         if (navItem.children && navItem.children.length > 0) {
669 |           processNavItems(navItem.children, groupNode.children, extractValue);
670 |         }
671 |       } else if (navItem.children && navItem.children.length > 0) {
672 |         // --- If no label but has children, still process them under parent
673 |         processNavItems(navItem.children, parentHierarchy, extractValue);
674 |       }
675 |     }
676 |   });
677 | }
678 | 
679 | // --- Extract navigation panel items from Pages component
680 | function extractNavPanelFromPages(
681 |   Pages: ComponentDef,
682 |   NavPanel: ComponentDef | undefined,
683 |   processedNavRef: React.MutableRefObject<boolean>,
684 |   extractValue: (value: any) => any,
685 |   parseHierarchyLabels: (labelText: string) => string[],
686 |   labelExistsInHierarchy: (searchLabel: string, hierarchy: NavHierarchyNode[]) => boolean,
687 |   findOrCreateNavGroup: (navItems: ComponentDef[], groupLabel: string) => ComponentDef,
688 | ): ComponentDef[] | null {
689 |   // --- Skip extraction if we've already processed this navigation structure
690 |   // --- This prevents duplicate items when React renders twice in strict mode
691 |   if (!Pages || processedNavRef.current) return null;
692 | 
693 |   // --- Mark as processed
694 |   processedNavRef.current = true;
695 | 
696 |   const extraNavs: ComponentDef[] = [];
697 | 
698 |   // --- Root of navigation hierarchy
699 |   const navigationHierarchy: NavHierarchyNode[] = [];
700 | 
701 |   // --- Start processing the navigation tree if NavPanel exists
702 |   if (NavPanel?.children) {
703 |     processNavItems(NavPanel.children, navigationHierarchy, extractValue);
704 |   }
705 | 
706 |   // --- Process Pages to create hierarchical navigation structure
707 |   Pages.children?.forEach((page) => {
708 |     if (page.type === "Page" && page.props.navLabel) {
709 |       const label = extractValue(page.props.navLabel);
710 |       const url = extractValue(page.props.url);
711 | 
712 |       // --- Parse hierarchy labels separated by unescaped pipe characters
713 |       const hierarchyLabels = parseHierarchyLabels(label);
714 | 
715 |       // --- Skip if we have no labels
716 |       if (hierarchyLabels.length === 0) {
717 |         return;
718 |       }
719 | 
720 |       // --- For a single level, just add a NavLink directly
721 |       if (hierarchyLabels.length === 1) {
722 |         // --- Check if this exact NavLink already exists in the navigation hierarchy
723 |         if (!labelExistsInHierarchy(hierarchyLabels[0], navigationHierarchy)) {
724 |           extraNavs.push({
725 |             type: "NavLink",
726 |             props: {
727 |               label: hierarchyLabels[0],
728 |               to: url,
729 |             },
730 |           });
731 |         }
732 |         return;
733 |       }
734 | 
735 |       // --- For multi-level hierarchies, create NavGroups and a final NavLink
736 |       let currentLevel = extraNavs;
737 | 
738 |       // --- Create NavGroups for all levels except the last one
739 |       for (let i = 0; i < hierarchyLabels.length - 1; i++) {
740 |         const groupLabel = hierarchyLabels[i];
741 |         const navGroup = findOrCreateNavGroup(currentLevel, groupLabel);
742 | 
743 |         // --- Initialize children array if it doesn't exist
744 |         if (!navGroup.children) {
745 |           navGroup.children = [];
746 |         }
747 | 
748 |         // --- Move to the next level
749 |         currentLevel = navGroup.children;
750 |       }
751 | 
752 |       // --- Add the leaf NavLink to the deepest NavGroup
753 |       const leafLabel = hierarchyLabels[hierarchyLabels.length - 1];
754 | 
755 |       // --- Check if this NavLink already exists at this level
756 |       const existingNavLink = currentLevel.find(
757 |         (item) => item.type === "NavLink" && item.props?.label === leafLabel,
758 |       );
759 | 
760 |       if (!existingNavLink) {
761 |         currentLevel.push({
762 |           type: "NavLink",
763 |           props: {
764 |             label: leafLabel,
765 |             to: url,
766 |           },
767 |         });
768 |       }
769 |     }
770 |   });
771 | 
772 |   return extraNavs;
773 | }
774 | 
```
Page 107/190FirstPrevNextLast