#
tokens: 45290/50000 2/1626 files (page 103/141)
lines: off (toggle) GitHub
raw markdown copy
This is page 103 of 141. Use http://codebase.md/xmlui-org/xmlui/tools/vscode/resources/%7Bsrc%7D?lines=false&page={x} to view the full context.

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/docs/public/pages/build-editor-component.md:
--------------------------------------------------------------------------------

```markdown
# Build a TableEditor Component


This guide walks you through building a `TableEditor` component for XMLUI, using the [Tiptap](https://tiptap.dev) editor as a foundation. It will provide a visual and Markdown-friendly way to create and edit tables for use in XMLUI-powered documentation.

> [!INFO]
> If you operate in the [XMLUI](https://github.com/xmlui-org/xmlui) repo you can test your work live. Follow the instructions in `dev-docs/next/generating-component-reference.md` to build the XMLUI docs site, then load localhost:5173. When you edit `.tsx` files they will automatically recompile, so you can iterate rapidly as you develop your component. And you can add a test page to the site in order to use your evolving component


## Step 1: Create the folder.

```bash
mkdir -p xmlui/src/components/TableEditor
```

## Step 2: Create a minimal renderer

Add `TableEditor.tsx` in that folder.

```xmlui
export function TableEditor() {
  return (
    <table>
      <thead>
        <tr>
          <th>Fruit</th>
          <th>Color</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Apple</td>
          <td>Red</td>
        </tr>
        <tr>
          <td>Banana</td>
          <td>Yellow</td>
        </tr>
      </tbody>
    </table>
  );
}

export const editorComponentRenderer = {
  type: "TableEditor",
  renderer: (props: any) => <TableEditor {...props} />
};
```

Register it in `ComponentProvider.tsx`.

```xmlui
import { editorComponentRenderer as TableEditorRenderer }
  from "./components/TableEditor/TableEditor";

if (process.env.VITE_USED_COMPONENTS_TableEditor !== "false") {
  this.registerCoreComponent(TableEditorRenderer);
}
```

You can now use this XMLUI markup.

```xmlui
<App>
  <TableEditor />
</App>
```

![](/resources/devdocs/table_editor_01.png)

We are just rendering an HTML table at this point, with no XMLUI theme support and no editing capability.

## Step 3: Make the table editable with Tiptap

Install Tiptap dependencies.

```
 npm install @tiptap/react @tiptap/starter-kit \
   @tiptap/extension-table @tiptap/extension-table-row \
   @tiptap/extension-table-cell @tiptap/extension-table-header
```

Update `TableEditor.tsx`.

```js filename="TableEditor.tsx" copy
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Table from "@tiptap/extension-table";
import TableRow from "@tiptap/extension-table-row";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";

export function TableEditor() {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
      <table>
        <thead>
          <tr>
            <th>Fruit</th>
            <th>Color</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Apple</td>
            <td>Red</td>
          </tr>
          <tr>
            <td>Banana</td>
            <td>Yellow</td>
          </tr>
        </tbody>
      </table>
    `,
  });

  return <EditorContent editor={editor} />;
}

export const editorComponentRenderer = {
  type: "TableEditor",
  renderer: (props: any) => <TableEditor {...props} />
};
```

Use the same XMLUI markup.

```xmlui
<App>
  <TableEditor />
</App>
```

You can now edit the cells in the table.


![](/resources/devdocs/table_editor_02.png)


## Step 4: Add an Insert Row button

```js {41-46} copy filename="TableEditor.tsx"
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Table from "@tiptap/extension-table";
import TableRow from "@tiptap/extension-table-row";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";

export function TableEditor() {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
      <table>
        <thead>
          <tr>
            <th>Fruit</th>
            <th>Color</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Apple</td>
            <td>Red</td>
          </tr>
          <tr>
            <td>Banana</td>
            <td>Yellow</td>
          </tr>
        </tbody>
      </table>
    `,
  });

  return (
    <div>
      <button
        onClick={() => editor && editor.commands.addRowAfter()}
        disabled={!editor}
      >
        Insert Row
      </button>
      <EditorContent editor={editor} />
    </div>
  );
}

export const editorComponentRenderer = {
  type: "TableEditor",
  renderer: (props: any) => <TableEditor {...props} />
};
```

Use the same XMLUI markup.

```xmlui
<App>
  <TableEditor />
</App>
```

You can now insert rows.

![](/resources/devdocs/table_editor_03.png)

## Step 5: Use XMLUI's Button

Import and use `Button`.

```js /Button/ copy filename="TableEditor.tsx"
  import { useEditor, EditorContent } from "@tiptap/react";
  import StarterKit from "@tiptap/starter-kit";
  import Table from "@tiptap/extension-table";
  import TableRow from "@tiptap/extension-table-row";
  import TableCell from "@tiptap/extension-table-cell";
  import TableHeader from "@tiptap/extension-table-header";
  import { Button } from "../Button/ButtonNative";

  export function TableEditor() {
    const editor = useEditor({
      extensions: [
        StarterKit,
        Table.configure({ resizable: true }),
        TableRow,
        TableHeader,
        TableCell,
      ],
      content: `
        <table>
          <thead>
            <tr>
              <th>Fruit</th>
              <th>Color</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Apple</td>
              <td>Red</td>
            </tr>
            <tr>
              <td>Banana</td>
              <td>Yellow</td>
            </tr>
          </tbody>
        </table>
      `,
    });

    return (
      <div>
        <Button
          onClick={() => editor && editor.commands.addRowAfter()}
          disabled={!editor}
        >
          Insert Row
        </Button>
        <EditorContent editor={editor} />
      </div>
    );
  }

  export const editorComponentRenderer = {
    type: "TableEditor",
    renderer: (props: any) => <TableEditor {...props} />
  };
```

You now have a proper themed XMLUI button.

![](/resources/devdocs/table_editor_04.png)

## Step 6: Show the HTML

Tiptap works natively with HTML, not Markdown. We'll eventually show Markdown but first let's add a method to show the HTML.

To keep our code modular, we'll separate the editor's rendering logic into a minimal presentational component. This makes it easy to reuse the editor UI in different contexts.

Create `TableEditorNative.tsx` alongside `TableEditor.tsx`.

```js filename="TableEditorNative.tsx" copy
import { EditorContent } from "@tiptap/react";

export function TableEditorNative({ editor }: { editor: any }) {
  return <EditorContent editor={editor} />;
}
```

This component simply renders the Tiptap editor UI for a given editor instance.

Now, let's expose a method to get the current HTML from the editor. Add this to `TableEditor.tsx`.

```js filename="TableEditorNative.tsx" copy
React.useEffect(() => {
  if (registerComponentApi && editor) {
    registerComponentApi({
      getHtmlSource: () => editor.getHTML(),
    });
  }
}, [registerComponentApi, editor]);
```

Now we can show the HTML using this markup.

```xmlui
<App>

  <TableEditor id="tableEditor" />

  <Text> { tableEditor.getHtmlSource() } </Text>

</App>
```

![](/resources/devdocs/table_editor_05.png)


## Step 7: Show the Markdown

Tiptap does not provide an HTML-to-Markdown  converter. We'll start with a basic one called `turndown`. First install it.

```
npm install turndown
```

Then update `TableEditor.tsx` to use it.

```js /turndown/ {42-60} filename="TableEditor.tsx"
import React from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Table from "@tiptap/extension-table";
import TableRow from "@tiptap/extension-table-row";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import { Button } from "../Button/ButtonNative";
import TurndownService from "turndown";

export function TableEditor({ registerComponentApi }: { registerComponentApi?: (api: any) => void }) {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
      <table>
        <thead>
          <tr>
            <th>Fruit</th>
            <th>Color</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Apple</td>
            <td>Red</td>
          </tr>
          <tr>
            <td>Banana</td>
            <td>Yellow</td>
          </tr>
        </tbody>
      </table>
    `,
  });

  React.useEffect(() => {
    if (registerComponentApi && editor) {
      const turndownService = new TurndownService();
      turndownService.addRule('table', {
        filter: 'table',
        replacement: function (content, node) {
          let rows = [];
          for (let row of node.querySelectorAll('tr')) {
            let cells = Array.from(row.children).map(cell => cell.textContent.trim());
            rows.push('| ' + cells.join(' | ') + ' |');
          }
          if (rows.length > 1) {
            // Add a separator after the header row
            const headerSep = '| ' + rows[0].split('|').slice(1, -1).map(() => '---').join(' | ') + ' |';
            rows.splice(1, 0, headerSep);
          }
          return rows.join('\n') + '\n';
        }
      });
      registerComponentApi({
        getHtmlSource: () => editor.getHTML(),
        getMarkdownSource: () => turndownService.turndown(editor.getHTML()),
      });
    }
  }, [registerComponentApi, editor]);

  return (
    <div>
      <Button
        onClick={() => editor && editor.commands.addRowAfter()}
        disabled={!editor}
      >
        Insert Row
      </Button>
      <EditorContent editor={editor} />
    </div>
  );
}

export const editorComponentRenderer2 = {
  type: "TableEditor",
  renderer: ({ registerComponentApi, ...props }: any) => (
    <TableEditor {...props} registerComponentApi={registerComponentApi} />
  ),
};
```

Now we can show the Markdown.

```xmlui
<App>

  <TableEditor id="tableEditor" />

  <Text variant="codefence" preserveLinebreaks="{true}">
    { tableEditor.getMarkdownSource() }
  </Text>

</App>
```

![](/resources/devdocs/table_editor_06.png)

Rendered in XMLUI Markdown:

<Image src="/resources/devdocs/xmlui-rendering-of-tiptap-markdown.png" width="200px"/>


## Step 8: Add controls

We can improve the TableEditor by adding more table editing controls like Insert Column, Delete Row, and Delete Column. But where should these controls live?

1. **TableEditorNative.tsx** (lowest level)
   - Pros: Closest to the editor instance
   - Cons: Not idiomatic for XMLUI; the "native" component should be minimal
2. **TableEditor.tsx** (middle level)
   - Pros: Provides a good default "batteries included" experience; keeps the native component minimal; still allows advanced users to build custom controls if needed
   - Cons: Slightly less flexible than fully user-defined controls
3. **User-defined component** (highest level)
   - Pros: Maximum flexibility for users
   - Cons: Every user has to reimplement the same basic controls; not user-friendly

We chose to implement the controls in `TableEditor.tsx` because it provides the best balance of usability and flexibility. Users get a working table editor with sensible controls out of the box, while advanced users can still build custom UIs using the exposed API if needed.

```js {71-87} filename="TableEditorNative.tsx"
import React from "react";
import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Table from "@tiptap/extension-table";
import TableRow from "@tiptap/extension-table-row";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import { Button } from "../Button/ButtonNative";
import { Stack } from "../Stack/StackNative";
import TurndownService from "turndown";
import { TableEditorNative } from "./TableEditorNative";

export function TableEditor({ registerComponentApi }: { registerComponentApi?: (api: any) => void }) {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
      <table>
        <thead>
          <tr>
            <th>Fruit</th>
            <th>Color</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Apple</td>
            <td>Red</td>
          </tr>
          <tr>
            <td>Banana</td>
            <td>Yellow</td>
          </tr>
        </tbody>
      </table>
    `,
  });

  React.useEffect(() => {
    if (registerComponentApi && editor) {
      const turndownService = new TurndownService();
      turndownService.addRule('table', {
        filter: 'table',
        replacement: function (content, node) {
          let rows = [];
          for (let row of node.querySelectorAll('tr')) {
            let cells = Array.from(row.children).map(cell => (cell as HTMLElement).textContent?.trim() ?? "");
            rows.push('| ' + cells.join(' | ') + ' |');
          }
          if (rows.length > 1) {
            // Add a separator after the header row
            const headerSep = '| ' + rows[0].split('|').slice(1, -1).map(() => '---').join(' | ') + ' |';
            rows.splice(1, 0, headerSep);
          }
          return rows.join('\n') + '\n';
        }
      });
      registerComponentApi({
        getHtmlSource: () => editor.getHTML(),
        getMarkdownSource: () => turndownService.turndown(editor.getHTML()),
      });
    }
  }, [registerComponentApi, editor]);

  return (
    <>
      <Stack orientation="horizontal">
        <Button onClick={() => editor && editor.commands.addRowAfter()} disabled={!editor}>
          Insert Row
        </Button>
        <Button onClick={() => editor && editor.commands.deleteRow()} disabled={!editor}>
          Delete Row
        </Button>
        <Button onClick={() => editor && editor.commands.addColumnAfter()} disabled={!editor}>
          Insert Column
        </Button>
        <Button onClick={() => editor && editor.commands.deleteColumn()} disabled={!editor}>
          Delete Column
        </Button>
      </Stack>
      <TableEditorNative editor={editor} />
    </>
  );
}

export const editorComponentRenderer = {
  type: "TableEditor",
  renderer: ({ registerComponentApi, ...props }: any) => (
    <TableEditor {...props} registerComponentApi={registerComponentApi} />
  ),
};
```

We import and use `Stack` from `../Stack/StackNative`. Note that `HStack` is not available as a native component, so we can't use that shortcut.

A component must return a single element. We use a React Fragment (`<>...</>`) to group the buttons into an anonymous container.

Now we have this result.

```xmlui
<App>

  <TableEditor id="tableEditor" />

  <Text variant="codefence" preserveLinebreaks="{true}">
    { tableEditor.getMarkdownSource() }
  </Text>

</App>
```

![](/resources/devdocs/table_editor_07.png)

## Step 9: Add custom icons

The table editor buttons currently use only text labels. Let's add custom SVG icons.

```js copy
<Button
  onClick={() => editor && editor.commands.addRowAfter()}
  disabled={!editor}
  themeColor={themeColor}
  variant={variant}
  size={size}
  orientation="horizontal"
>
  <svg
    viewBox="0 0 24 16"
    stroke="currentColor"
    fill="none"
    strokeWidth="1.5"
    strokeLinecap="round"
    strokeLinejoin="round"
  >
    <rect x="1.5" y="1.5" width="13" height="11" rx="1" />
    <line x1="1.5" y1="5.5" x2="14.5" y2="5.5" />
    <line x1="1.5" y1="9.5" x2="14.5" y2="9.5" />
    <line x1="19" y1="6" x2="19" y2="10" />
    <line x1="17" y1="8" x2="21" y2="8" />
  </svg>
  Insert Row
</Button>
```

We'll repeat this pattern for the other buttons.

<Image src="/resources/devdocs/table-editor-07.png" width="600px"/>

## Step 10: Theme the buttons

Our TableEditor component currently uses hardcoded button styling. To make TableEditor behave like a proper XMLUI component, we need to add theme support.

Try adding different theme props to your TableEditor:

```xmlui
<TableEditor themeColor="secondary" variant="outlined" size="lg" />
```

The buttons remain blue and solid. This happens because XMLUI doesn't know which props are valid for custom components. Without proper metadata, theme attributes are filtered out.

XMLUI components need metadata to define their allowed props.

```js {12-15} /TableEditorMd/ filename="TableEditorNative.tsx"
import React from "react";
import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Table from "@tiptap/extension-table";
import TableRow from "@tiptap/extension-table-row";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import { Button } from "../Button/ButtonNative";
import { Stack } from "../Stack/StackNative";
import TurndownService from "turndown";
import { TableEditorNative } from "./TableEditorNative";
import { createMetadata } from "../metadata-helpers";
import { createComponentRenderer } from "../../components-core/renderers";
import { buttonThemeMd, buttonVariantMd, sizeMd } from "../abstractions";

export function TableEditor({
  registerComponentApi,
  themeColor = "primary",
  variant = "solid",
  size = "sm",
}: {
  registerComponentApi?: (api: any) => void;
  themeColor?: "primary" | "secondary" | "attention";
  variant?: "solid" | "outlined" | "ghost";
  size?: "xs" | "sm" | "md" | "lg";
}) {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
      <table>
        <thead>
          <tr>
            <th>Fruit</th>
            <th>Color</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Apple</td>
            <td>Red</td>
          </tr>
          <tr>
            <td>Banana</td>
            <td>Yellow</td>
          </tr>
        </tbody>
      </table>
    `,
  });

  React.useEffect(() => {
    if (registerComponentApi && editor) {
      const turndownService = new TurndownService();
      turndownService.addRule('table', {
        filter: 'table',
        replacement: function (content, node) {
          let rows = [];
          for (let row of node.querySelectorAll('tr')) {
            let cells = Array.from(row.children).map(cell => (cell as HTMLElement).textContent?.trim() ?? "");
            rows.push('| ' + cells.join(' | ') + ' |');
          }
          if (rows.length > 1) {
            // Add a separator after the header row
            const headerSep = '| ' + rows[0].split('|').slice(1, -1).map(() => '---').join(' | ') + ' |';
            rows.splice(1, 0, headerSep);
          }
          return rows.join('\n') + '\n';
        }
      });
      registerComponentApi({
        getHtmlSource: () => editor.getHTML(),
        getMarkdownSource: () => turndownService.turndown(editor.getHTML()),
      });
    }
  }, [registerComponentApi, editor]);

  return (
    <>
      <Stack orientation="horizontal">
        <Button onClick={() => editor && editor.commands.addRowAfter()} disabled={!editor} themeColor={themeColor} variant={variant} size={size}>
          <svg
            viewBox="0 0 24 16"
            stroke="currentColor"
            fill="none"
            strokeWidth="1.5"
            strokeLinecap="round"
            strokeLinejoin="round"
            width="20"
            height="16"
            style={{ marginRight: 4 }}
          >
            <rect x="1.5" y="1.5" width="13" height="11" rx="1" />
            <line x1="1.5" y1="5.5" x2="14.5" y2="5.5" />
            <line x1="1.5" y1="9.5" x2="14.5" y2="9.5" />
            <line x1="19" y1="6" x2="19" y2="10" />
            <line x1="17" y1="8" x2="21" y2="8" />
          </svg>
          Insert Row
        </Button>
        <Button onClick={() => editor && editor.commands.deleteRow()} disabled={!editor} themeColor={themeColor} variant={variant} size={size}>
          <svg
            viewBox="0 0 24 16"
            stroke="currentColor"
            fill="none"
            strokeWidth="1.5"
            strokeLinecap="round"
            strokeLinejoin="round"
            width="20"
            height="16"
            style={{ marginRight: 4 }}
          >
            <rect x="1.5" y="1.5" width="13" height="11" rx="1" />
            <line x1="1.5" y1="5.5" x2="14.5" y2="5.5" />
            <line x1="1.5" y1="9.5" x2="14.5" y2="9.5" />
            <line x1="17" y1="8" x2="21" y2="8" />
          </svg>
          Delete Row
        </Button>
        <Button onClick={() => editor && editor.commands.addColumnAfter()} disabled={!editor} themeColor={themeColor} variant={variant} size={size}>
          <svg
            viewBox="0 0 24 16"
            stroke="currentColor"
            fill="none"
            strokeWidth="1.5"
            strokeLinecap="round"
            strokeLinejoin="round"
            width="24"
            height="16"
            style={{ marginRight: 4 }}
          >
            <rect x="1.5" y="1.5" width="13" height="11" rx="1" />
            <line x1="5.5" y1="3.5" x2="5.5" y2="11.5" />
            <line x1="9" y1="3.5" x2="9" y2="11.5" />
            <line x1="19" y1="6.5" x2="19" y2="9.5" />
            <line x1="17.5" y1="8" x2="20.5" y2="8" />
          </svg>
          Insert Column
        </Button>
        <Button onClick={() => editor && editor.commands.deleteColumn()} disabled={!editor} themeColor={themeColor} variant={variant} size={size}>
          <svg
            viewBox="0 0 24 16"
            stroke="currentColor"
            fill="none"
            strokeWidth="1.5"
            strokeLinecap="round"
            strokeLinejoin="round"
            width="24"
            height="16"
            style={{ marginRight: 4 }}
          >
            <rect x="1.5" y="1.5" width="13" height="11" rx="1" />
            <line x1="5.5" y1="3.5" x2="5.5" y2="11.5" />
            <line x1="9" y1="3.5" x2="9" y2="11.5" />
            {/* Minus sign for delete */}
            <line x1="17" y1="8" x2="21" y2="8" />
          </svg>
          Delete Column
        </Button>
      </Stack>
      <TableEditorNative editor={editor} />
    </>
  );
}

export const TableEditorMd = createMetadata({
  description:
    "`TableEditor` provides an interactive table editing interface with controls for adding and deleting rows and columns. It supports theme customization and exports table data in HTML and Markdown formats.",
  status: "experimental",
  props: {
    themeColor: {
      description: "Sets the color scheme for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: buttonThemeMd,
      defaultValue: "primary",
    },
    variant: {
      description: "Sets the visual style for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: buttonVariantMd,
      defaultValue: "solid",
    },
    size: {
      description: "Sets the size for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: sizeMd,
      defaultValue: "sm",
    },
  },
  events: {},
});

export const editorComponentRenderer = createComponentRenderer(
  "TableEditor",
  TableEditorMd,
  ({ node, extractValue, registerComponentApi }) => (
    <TableEditor
      themeColor={extractValue.asOptionalString(node.props.themeColor)}
      variant={extractValue.asOptionalString(node.props.variant)}
      size={extractValue.asOptionalString(node.props.size)}
      registerComponentApi={registerComponentApi}
    />
  ),
);
```

We add the necessary imports to `TableEditor.tsx`.

```js filename="TableEditor.tsx" copy
import { createMetadata } from "../metadata-helpers";
import { createComponentRenderer } from "../../components-core/renderers";
import { buttonThemeMd, buttonVariantMd, sizeMd } from "../abstractions";
```

We modify the TableEditor function to accept theme props.

```js filename="TableEditor.tsx" copy
export function TableEditor({
  registerComponentApi,
  themeColor = "primary",
  variant = "solid",
  size = "sm",
}: {
  registerComponentApi?: (api: any) => void;
  themeColor?: "primary" | "secondary" | "attention";
  variant?: "solid" | "outlined" | "ghost";
  size?: "xs" | "sm" | "md" | "lg";
}) {
  // ... existing logic
}
```

We forward these props to all Button components.

```js copy
<Button
  onClick={() => editor && editor.commands.addRowAfter()}
  disabled={!editor}
  themeColor={themeColor}
  variant={variant}
  size={size}
>
  Insert Row
</Button>
```

We add metadata that defines the allowed props:

```js copy filename="TableEditor.tsx"
export const TableEditorMd = createMetadata({
  description:
    "`TableEditor` provides an interactive table editing interface with controls for adding and deleting rows and columns. It supports theme customization and exports table data in HTML and Markdown formats.",
  status: "stable",
  props: {
    themeColor: {
      description: "Sets the color scheme for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: buttonThemeMd,
      defaultValue: "primary",
    },
    variant: {
      description: "Sets the visual style for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: buttonVariantMd,
      defaultValue: "solid",
    },
    size: {
      description: "Sets the size for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: sizeMd,
      defaultValue: "sm",
    },
  },
  events: {},
});
```

We replace the simple renderer with a proper one that uses metadata:

```js filename="TableEditor.tsx" copy
export const editorComponentRenderer = createComponentRenderer(
  "TableEditor",
  TableEditorMd,
  ({ node, extractValue, registerComponentApi }) => {
    return (
      <TableEditor
        themeColor={extractValue.asOptionalString(node.props.themeColor)}
        variant={extractValue.asOptionalString(node.props.variant)}
        size={extractValue.asOptionalString(node.props.size)}
        registerComponentApi={registerComponentApi}
      />
    );
  },
);
```

Now TableEditor supports full theme customization.

```xmlui copy
  <TableEditor
    id="tableEditor"
  />

  <TableEditor
    id="tableEditor2"
    themeColor="primary"
    size="lg"
    variant="outlined"
  />

    <TableEditor
    id="tableEditor3"
    themeColor="attention"
    variant="ghost"
    size="sm"
  />
```

<Image src="/resources/devdocs/table_editor_08.png" />

## Step 11: Register the icons

> [!INFO]
> Registering icons is optional if you only use them inline within your component, but it is strongly recommended for reusability, maintainability, and accessibility.

**Global usage.** Registered icons can be referenced anywhere in your app or docs by name (e.g., `<Icon name="table-insert-row" />` or as a Button's `icon` prop).

**Accessibility.** Using the `<Icon />` component or Button's `icon` prop helps ensure ARIA attributes and accessible labeling are handled consistently.

To make your custom icon usable in XMLUI, wrap your SVG markup in a React component. This allows the icon to inherit color, size, and accessibility props from its parent.

```js copy
// File: xmlui/src/components/Icon/TableInsertRowIcon.tsx
import React from "react";

export default function TableInsertRowIcon(props) {
  return (
    <svg
      viewBox="0 0 24 16"
      stroke="currentColor"
      fill="none"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      {...props}  // Enables theming, sizing, and accessibility
    >
      <rect x="1.5" y="1.5" width="13" height="11" rx="1" />
      <line x1="1.5" y1="5.5" x2="14.5" y2="5.5" />
      <line x1="1.5" y1="9.5" x2="14.5" y2="9.5" />
      <line x1="19" y1="6" x2="19" y2="10" />
      <line x1="17" y1="8" x2="21" y2="8" />
    </svg>
  );
}
```

> [!INFO]
> The `{...props}` spread is essential—it lets XMLUI pass color, size, and ARIA attributes to your icon automatically.

Register the icon in `IconProvider.tsx`.

```js filename="IconProvider.tsx"
import TableInsertRowIcon from "./Icon/TableInsertRowIcon";

registerIconRenderer("table-insert-row", (props) => <TableInsertRowIcon {...props} />);
```

Update `TableEditor.tsx`.

```js /<Icon/ filename="TableEditor.tsx"
import React from "react";
import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Table from "@tiptap/extension-table";
import TableRow from "@tiptap/extension-table-row";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import { Button } from "../Button/ButtonNative";
import { Stack } from "../Stack/StackNative";
import TurndownService from "turndown";
import { TableEditorNative2 } from "./TableEditorNative2";
import { createMetadata } from "../metadata-helpers";
import { createComponentRenderer } from "../../components-core/renderers";
import { buttonThemeMd, buttonVariantMd, sizeMd } from "../abstractions";
import Icon from "../Icon/IconNative";

export function TableEditor({
  registerComponentApi,
  themeColor = "primary",
  variant = "solid",
  size = "sm",
}: {
  registerComponentApi?: (api: any) => void;
  themeColor?: "primary" | "secondary" | "attention";
  variant?: "solid" | "outlined" | "ghost";
  size?: "xs" | "sm" | "md" | "lg";
}) {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Table.configure({ resizable: true }),
      TableRow,
      TableHeader,
      TableCell,
    ],
    content: `
      <table>
        <thead>
          <tr>
            <th>Fruit</th>
            <th>Color</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Apple</td>
            <td>Red</td>
          </tr>
          <tr>
            <td>Banana</td>
            <td>Yellow</td>
          </tr>
        </tbody>
      </table>
    `,
  });

  React.useEffect(() => {
    if (registerComponentApi && editor) {
      const turndownService = new TurndownService();
      turndownService.addRule('table', {
        filter: 'table',
        replacement: function (content, node) {
          let rows = [];
          for (let row of node.querySelectorAll('tr')) {
            let cells = Array.from(row.children).map(cell => (cell as HTMLElement).textContent?.trim() ?? "");
            rows.push('| ' + cells.join(' | ') + ' |');
          }
          if (rows.length > 1) {
            // Add a separator after the header row
            const headerSep = '| ' + rows[0].split('|').slice(1, -1).map(() => '---').join(' | ') + ' |';
            rows.splice(1, 0, headerSep);
          }
          return rows.join('\n') + '\n';
        }
      });
      registerComponentApi({
        getHtmlSource: () => editor.getHTML(),
        getMarkdownSource: () => turndownService.turndown(editor.getHTML()),
      });
    }
  }, [registerComponentApi, editor]);

  return (
    <>
      <Stack orientation="horizontal">
        <Button onClick={() => editor && editor.commands.addRowAfter()} disabled={!editor}
          themeColor={themeColor} variant={variant} size={size}
          icon={<Icon name="table-insert-row" aria-hidden />} contextualLabel="Insert Row"
        >
          Insert Row
        </Button>
        <Button onClick={() => editor && editor.commands.deleteRow()} disabled={!editor}
          themeColor={themeColor} variant={variant} size={size}
          icon={<Icon name="table-delete-row" aria-hidden />} contextualLabel="Delete Row">
          Delete Row
        </Button>
        <Button onClick={() => editor && editor.commands.addColumnAfter()} disabled={!editor}
          themeColor={themeColor} variant={variant} size={size}
          icon={<Icon name="table-insert-column" aria-hidden />} contextualLabel="Insert Column">
          Insert Column
        </Button>
        <Button onClick={() => editor && editor.commands.deleteColumn()} disabled={!editor}
          themeColor={themeColor} variant={variant} size={size}
          icon={<Icon name="table-delete-column" aria-hidden />} contextualLabel="Delete Column">
          Delete Column
        </Button>
      </Stack>
      <TableEditorNative2 editor={editor} />
    </>
  );
}

export const TableEditorMd = createMetadata({
  description:
    "`TableEditor` provides an interactive table editing interface with controls for adding and deleting rows and columns. It supports theme customization and exports table data in HTML and Markdown formats.",
  status: "experimental",
  props: {
    themeColor: {
      description: "Sets the color scheme for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: buttonThemeMd,
      defaultValue: "primary",
    },
    variant: {
      description: "Sets the visual style for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: buttonVariantMd,
      defaultValue: "solid",
    },
    size: {
      description: "Sets the size for all editor buttons.",
      isRequired: false,
      type: "string",
      availableValues: sizeMd,
      defaultValue: "sm",
    },
  },
  events: {},
});

export const editorComponentRenderer = createComponentRenderer(
  "TableEditor",
  TableEditorMd,
  ({ node, extractValue, registerComponentApi }) => (
    <TableEditor2
      themeColor={extractValue.asOptionalString(node.props.themeColor)}
      variant={extractValue.asOptionalString(node.props.variant)}
      size={extractValue.asOptionalString(node.props.size)}
      registerComponentApi={registerComponentApi}
    />
  ),
);
```

For icon-only buttons, use the `contextualLabel` prop to provide an accessible name.

```xmlui
<Button icon="table-insert-row" contextualLabel="Insert a new row" />
```

This ensures screen readers announce the button's purpose, even if only the icon is visible.

For decorative icons, use `aria-hidden="true"` to hide the icon from assistive technology.

```xmlui
<Icon name="table-insert-row" aria-hidden="true" />
```

## Step 12: Make the Markdown reactive

We want the Markdown display to update when changes happen in the editor. Make this change in `TableEditor.tsx`.

```js {6} {8-13} filename="TableEditor.tsx"
React.useEffect(() => {
  if (!editor) return;
  const handler = () => {
    const html = editor.getHTML();
    const markdown = turndownService.turndown(html);
    onDidChange({ html, markdown });
  };
  editor.on("update", handler);
  // Emit once on mount
  handler();
  return () => {
    editor.off("update", handler);
  };
}, [editor, onDidChange]);
```

Replace `events: {}` with this snippet.

```js
events: {
  didChange: {
    description: "Fired whenever the table content changes. Payload: { html, markdown }.",
    isRequired: false,
    type: "function",
  },
},
```

And do this.

```js {4-5} {12} copy
export const editorComponentRenderer = createComponentRenderer(
  "TableEditor",
  TableEditorMd,
({ node, extractValue, registerComponentApi, lookupEventHandler }) => {
  const handler = lookupEventHandler?.("didChange");
    return (
      <TableEditor
        themeColor={extractValue.asOptionalString(node.props.themeColor)}
        variant={extractValue.asOptionalString(node.props.variant)}
        size={extractValue.asOptionalString(node.props.size)}
        registerComponentApi={registerComponentApi}
        onDidChange={handler}
      />
    );
  },
)
```

Now the Markdown updates as you change the table.

<Image src="/resources/devdocs/table_editor_09.png" />

## Step 13: Style the editor

We'll want to control the editor's style with XMLUI theme variables, but first let's add basic styling in `TableEditor.module.scss`.

```css filename="TableEditor.module.css" copy
.table-editor-root {
  padding: 8px;
  overflow-x: auto;
}

.ProseMirror {
  outline: none;
  white-space: pre-wrap;
  word-break: break-word;
  min-height: 100px;
}

.ProseMirror table {
  border-collapse: collapse;
}
.ProseMirror th,
.ProseMirror td {
  border: 1px solid #ccc;
  padding: 4px;
}

.button-stack {
  margin-bottom: 16px;
}
```

Use the styles in `TableEditor.tsx`.

```js filename="TableEditor.tsx"
return (
  <div className="table-editor-root">
    <div className="button-stack">
      <Stack orientation="horizontal">
      ...
      </Stack>
    </div>
    <TableEditorNative editor={editor} />
  </div>
);
```

Use this XMLUI markup.

```xmlui
<App var.markdown="">

  <Card>
    <TableEditor
      id="tableEditor"
      size="xs"
      onDidChange="{(e) => { markdown = e.markdown }}"
    />
  </Card>

  <Card>
    <Text variant="codefence" preserverLinebreaks="{true}">
      { markdown }
    </Text>
  </Card>
</App>
```

Here is the result.

<Image src="/resources/devdocs/table_editor_10.png" />

## Step 14: Theme the editor

Now let's make `TableEditor` visually consistent with [Table](/components/Table) by reusing XMLUI theme variables for header, cell, and spacing styles. Here is the new `TableEditor.module.css`.

```scss filename="TableEditor.module.css" copy
@use "../../components-core/theming/themes" as t;
$componentName: "TableEditor";

$themeVars: ();
@function createThemeVar($componentVariable) {
  $themeVars: t.appendThemeVar($themeVars, $componentVariable) !global;
  @return t.getThemeVar($themeVars, $componentVariable);
}

// Reuse Table theme variables
$backgroundColorHeadingTable: createThemeVar("backgroundColor-heading-Table");
$borderCellTable: createThemeVar("border-cell-Table");
$fontSizeHeadingTable: createThemeVar("fontSize-heading-Table");
$fontWeightHeadingTable: createThemeVar("fontWeight-heading-Table");
$fontSizeRowTable: createThemeVar("fontSize-row-Table");
$fontWeightRowTable: createThemeVar("fontWeight-row-Table");
$paddingCellTable: createThemeVar("padding-cell-Table");
$paddingHeadingTable: createThemeVar("padding-heading-Table");
$textColorHeadingTable: createThemeVar("textColor-heading-Table");
$textColorPaginationTable: createThemeVar("textColor-pagination-Table");
$buttonStackSpacing: createThemeVar("spacing-buttonStack-Table");
$textTransformHeadingTable: createThemeVar("textTransform-heading-Table");
$tableMarginTop: t.$space-4;
$paddingHtmlTd: createThemeVar("padding-HtmlTd");
$paddingHtmlTh: createThemeVar("padding-HtmlTh");

.editor {
  :global(.ProseMirror) {
    outline: none;
    white-space: pre-wrap;
    word-break: break-word;
    min-height: 100px;
    table {
      border-collapse: collapse;
      margin-top: $tableMarginTop;
    }
    th {
      background-color: $backgroundColorHeadingTable;
      font-size: $fontSizeHeadingTable;
      font-weight: $fontWeightHeadingTable;
      padding: $paddingHtmlTh;
      color: $textColorHeadingTable;
      text-transform: $textTransformHeadingTable;
    }
    td {
      font-size: $fontSizeRowTable;
      font-weight: $fontWeightRowTable;
      padding: $paddingHtmlTd;
      border: $borderCellTable;
    }
  }
}

.button-stack {
  margin-bottom: $buttonStackSpacing;
}

.table-editor-root {
  padding: 8px;
  overflow-x: auto;
}

.pagination {
  color: $textColorPaginationTable;
}

:export {
  themeVars: t.json-stringify($themeVars);
}
```

Now the editor matches the look of XMLUI tables rendered directly using `Table` or indirectly when tables appear in a XMLUI `Markdown` context. Try it!

```xmlui-pg display noHeader
<App var.markdown="">

  <Card>
    <TableEditor
      id="tableEditor"
      size="xs"
      onDidChange="{(e) => { markdown = e.markdown }}"
    />
  </Card>

  <Card>
    <HStack>
      <Text variant="codefence" preserveLinebreaks="{true}">
        { markdown }
      </Text>
      <SpaceFiller />
      <Button
        icon="copy"
        variant="ghost"
        size="xs"
        onClick="navigator.clipboard.writeText(markdown)"
      />
    </HStack>
  </Card>

</App>
```

<!--
<Image src="/resources/devdocs/table_editor_11.png" />
-->



```

--------------------------------------------------------------------------------
/xmlui/src/components-core/StandaloneApp.tsx:
--------------------------------------------------------------------------------

```typescript
import type { ReactNode } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import type { Root } from "react-dom/client";
import ReactDOM from "react-dom/client";
import yaml from "js-yaml";

import type { StandaloneAppDescription, StandaloneJsonConfig } from "./abstractions/standalone";
import type {
  ComponentDef,
  ComponentLike,
  CompoundComponentDef,
} from "../abstractions/ComponentDefs";

import "../index.scss";
import { AppRoot } from "./rendering/AppRoot";
import { normalizePath } from "./utils/misc";
import { ApiInterceptorProvider } from "./interception/ApiInterceptorProvider";
import { EMPTY_OBJECT } from "./constants";
import {
  errReportComponent,
  errReportMessage,
  errReportModuleErrors,
  errReportScriptError,
  xmlUiMarkupToComponent,
} from "./xmlui-parser";
import { useIsomorphicLayoutEffect } from "./utils/hooks";
import {
  codeBehindFileExtension,
  componentFileExtension,
} from "../parsers/xmlui-parser/fileExtensions";
import { Parser } from "../parsers/scripting/Parser";
import {
  collectCodeBehindFromSource,
  removeCodeBehindTokensFromTree,
} from "../parsers/scripting/code-behind-collect";
import { ComponentRegistry } from "../components/ComponentProvider";
import { checkXmlUiMarkup } from "./markup-check";
import StandaloneExtensionManager from "./StandaloneExtensionManager";
import { builtInThemes } from "./theming/ThemeProvider";
import type { Extension } from "../abstractions/ExtensionDefs";
import {
  getLintSeverity,
  lintApp,
  lintErrorsComponent,
  LintSeverity,
  printComponentLints,
} from "../parsers/xmlui-parser/lint";
import { collectedComponentMetadata } from "../components/collectedComponentMetadata";
import type { ThemeDefinition, ThemeTone } from "../abstractions/ThemingDefs";
import type {
  ComponentCompilation,
  ProjectCompilation,
} from "../abstractions/scripting/Compilation";
import { MetadataProvider } from "../language-server/services/common/metadata-utils";
import type { CollectedDeclarations } from "./script-runner/ScriptingSourceTree";

const MAIN_FILE = "Main." + componentFileExtension;
const MAIN_CODE_BEHIND_FILE = "Main." + codeBehindFileExtension;
const CONFIG_FILE = "config.json";

const metadataProvider = new MetadataProvider(collectedComponentMetadata);

type RuntimeProps = {
  default?: any;
  component?: ComponentDef | CompoundComponentDef;
  file?: string;
  src?: string;
}

// --- The properties of the standalone app
type StandaloneAppProps = {
  // --- The standalone app description (the engine renders this definition)
  appDef?: StandaloneAppDescription;
  appGlobals?: Record<string, any>;

  // --- In E2E tests, we can decorate the components with test IDs
  decorateComponentsWithTestId?: boolean;

  // --- Debugging can be enabled or disabled
  debugEnabled?: boolean;

  // --- The runtime environment of the standalone app (for pre-compiled apps)
  runtime?: RuntimeProps;

  // --- The object responsible for managing the standalone components
  extensionManager?: StandaloneExtensionManager;

  // --- If true, the app waits for the API interceptor to be ready
  waitForApiInterceptor?: boolean;
  children?: ReactNode;
};

/**
 * This React component represents a standalone app that implements a web
 * application with xmlui components. A StandaloneApp instance uses a
 * AppRoot wrapped into an ApiInterceptor.
 *
 * AppRoot is responsible for rendering the app (using an internal
 * representation); ApiInterceptor can emulate some backend functionality
 * running in the browser.
 */
function StandaloneApp({
  appDef,
  appGlobals: globals,
  decorateComponentsWithTestId,
  debugEnabled = false,
  runtime,
  extensionManager,
  waitForApiInterceptor = false,
  children,
}: StandaloneAppProps) {
  // --- Fetch all files constituting the standalone app, including components,
  // --- themes, and other artifacts. Display the app version numbers in the
  // --- console.
  const { standaloneApp, projectCompilation } = useStandalone(appDef, runtime, extensionManager);

  usePrintVersionNumber(standaloneApp);

  const {
    apiInterceptor,
    name,
    appGlobals,
    defaultTheme,
    defaultTone,
    resources,
    resourceMap,
    entryPoint,
    components,
    themes,
    sources,
  } = standaloneApp || {};

  const globalProps = useMemo(() => {
    return {
      name: name,
      ...(appGlobals || {}),
      ...(globals || {}),
    };
  }, [appGlobals, globals, name]);

  let contributes = useMemo(() => {
    return {
      compoundComponents: components,
      themes,
    };
  }, [components, themes]);

  if (!standaloneApp) {
    // --- Problems found, the standalone app cannot run
    return null;
  }

  // --- The app may use a mocked API already defined in `window.XMLUI_MOCK_API`
  // --- or within the standalone app's definition, in `apiInterceptor`.
  const mockedApi =
    // @ts-ignore
    typeof window !== "undefined" && window.XMLUI_MOCK_API ? window.XMLUI_MOCK_API : apiInterceptor;

  // --- Components can be decorated with test IDs used in end-to-end tests.
  // --- This flag checks the environment if the app runs in E2E test mode.
  const shouldDecorateWithTestId =
    decorateComponentsWithTestId ||
    // @ts-ignore
    (typeof window !== "undefined" ? window.XMLUI_MOCK_TEST_ID : false);

  // --- An app can turn off the default hash routing.
  const useHashBasedRouting = appGlobals?.useHashBasedRouting ?? true;

  return (
    <ApiInterceptorProvider
      interceptor={mockedApi}
      useHashBasedRouting={useHashBasedRouting}
      waitForApiInterceptor={waitForApiInterceptor}
    >
      <AppRoot
        projectCompilation={projectCompilation}
        decorateComponentsWithTestId={shouldDecorateWithTestId}
        node={entryPoint!}
        standalone={true}
        debugEnabled={debugEnabled}
        // @ts-ignore
        routerBaseName={typeof window !== "undefined" ? window.__PUBLIC_PATH || "" : ""}
        globalProps={globalProps}
        defaultTheme={defaultTheme}
        defaultTone={defaultTone as ThemeTone}
        resources={resources}
        resourceMap={resourceMap}
        sources={sources}
        extensionManager={extensionManager}
        contributes={contributes}
      >
        {children}
      </AppRoot>
    </ApiInterceptorProvider>
  );
}

// --- This type represents the parsed structure of a component markup of
// --- code-behind file (for further processing)
type ParsedResponse = {
  // --- The component definition (it may be a compound component)
  component?: ComponentDef | CompoundComponentDef;

  // --- The optional code-behind source code of the component
  codeBehind?: CollectedDeclarations;

  // --- The optional source code of the component (for debugging or learning purposes)
  src?: string;

  // --- The optional file name of the component (for debugging or learning purposes)
  file?: string;

  // --- The flag indicating if the component has errors
  hasError?: boolean;
};

/**
 * This function parses the response of a fetch retrieving the contents of a
 * component markup file.
 * @param response The response coming from the fetch
 * @returns If parsing is successful, it returns the parsed response containing
 * the component definition. Otherwise, it returns a component definition that
 * displays the errors.
 */
async function parseComponentMarkupResponse(response: Response): Promise<ParsedResponse> {
  if (!response.ok) {
    throw new Error(`Failed to fetch ${response.url}`);
  }
  const code = await response.text();
  const fileId = response.url;
  let { component, errors, erroneousCompoundComponentName } = xmlUiMarkupToComponent(code, fileId);
  if (errors.length > 0) {
    const compName =
      erroneousCompoundComponentName ??
      response.url.substring(
        response.url.lastIndexOf("/") + 1,
        response.url.length - ".xmlui".length,
      );
    component = errReportComponent(errors, fileId, compName);
  }
  return {
    component,
    src: code,
    file: fileId,
    hasError: errors.length > 0,
  };
}

/**
 * This function parses the response of a fetch retrieving the contents of a
 * code-behind file.
 * @param response The response coming from the fetch
 * @returns If parsing is successful, it returns the parsed response containing
 * the code-behind declarations. Otherwise, it returns a component definition that
 * displays the errors.
 */
async function parseCodeBehindResponse(response: Response): Promise<ParsedResponse> {
  if (!response.ok) {
    throw new Error(`Failed to fetch ${response.url}`);
  }
  const code = await response.text();
  const parser = new Parser(code);
  try {
    parser.parseStatements();
  } catch (e) {
    console.error("Error parsing code behind", e);
    // throw new Error(`Failed to fetch ${response.url}`);
    if (parser.errors.length > 0) {
      return {
        component: errReportScriptError(parser.errors[0], response.url),
        file: response.url,
        hasError: true,
      };
    }
  }

  try {
    const codeBehind = collectCodeBehindFromSource("Main", code);
    if (Object.keys(codeBehind.moduleErrors ?? {}).length > 0) {
      return {
        component: errReportModuleErrors(codeBehind.moduleErrors, response.url),
        file: response.url,
        hasError: true,
      };
    }

    // --- Remove the code-behind tokens from the tree shrinking the tree
    removeCodeBehindTokensFromTree(codeBehind);
    return {
      src: code,
      codeBehind: codeBehind,
      file: response.url,
    };
  } catch (e) {
    console.error("Error collecting code behind", e);
  }
}

// --- Tests is the given path matches the speified file name
function matchesFileName(path: string, fileName: string) {
  return (
    path.endsWith(`/${fileName}.ts`) ||
    path.endsWith(`/${fileName}.js`) ||
    path.endsWith(`/${fileName}.${componentFileExtension}`) ||
    path.endsWith(`/${fileName}.${codeBehindFileExtension}`)
  );
}

// --- Tests if the given path contains the specified folder
function matchesFolder(path: string, folderName: string) {
  return path.includes(`/${folderName}/`);
}

/**
 * This function turns a collection of runtime file declarations into a standalone
 * app description.
 * @param runtime A hash object containing the runtime files. The keys are the file
 * paths and the values are the file contents.
 * @returns The standalone app description.
 *
 * When the standalone app is pre-compiled, each property in `runtime` holds such a
 * pre-compiled item. Otherwise, `runtime` is an empty object.
 *
 * While processing the files here, we can assume they are free from compilation
 * errors, as such errors would be observed in the compile phase.
 */
function resolveRuntime(runtime: Record<string, any>): {
  standaloneApp: StandaloneAppDescription;
  projectCompilation?: ProjectCompilation;
} {
  // --- Collect the components and their sources. We pass the sources to the standalone app
  // --- so that it can be used for error display and debugging purposes.
  const projectCompilation: ProjectCompilation = {
    entrypoint: {
      filename: "",
      definition: null,
      dependencies: new Set(),
    },
    components: [],
    themes: {},
  };
  const sources: Record<string, string> = {};
  const componentsByFileName: Record<string, CompoundComponentDef> = {};
  const codeBehindsByFileName: Record<string, CollectedDeclarations> = {};
  const themes: Array<ThemeDefinition> = [];

  // --- Some working variables
  let config: StandaloneAppDescription | undefined;
  let entryPoint: ComponentDef | undefined;
  let entryPointCodeBehind: CollectedDeclarations | undefined;
  let apiInterceptor: any;

  // --- Process the runtime files
  for (const [key, value] of Object.entries(runtime)) {
    if (matchesFileName(key, "config")) {
      // --- We assume that the config file has a default export and this
      // --- export is the standalone app's configuration.
      config = value.default;
    }

    // --- We assume that the entry point is either named "Main" or "App".
    if (matchesFileName(key, "Main") || matchesFileName(key, "App")) {
      if (key.endsWith(codeBehindFileExtension)) {
        // --- "default" contains the functions and variables declared in the
        // --- code behind file.
        entryPointCodeBehind = value.default;
        projectCompilation.entrypoint.codeBehindSource = value.default.src;
      } else {
        projectCompilation.entrypoint.filename = key;
        // --- "default" contains the component definition, the file index,
        // --- and the source code.
        entryPoint = value.default.component;
        projectCompilation.entrypoint.definition = entryPoint;
        projectCompilation.entrypoint.markupSource = value.default.src;
        if (value.default.file) {
          // TODO: Remove this prop
          sources[value.default.file] = value.default.src;
        }
      }
    }

    // --- Use API emulation if available
    if (matchesFileName(key, "api")) {
      apiInterceptor = value.default;
    }

    // --- Collect the components and their code behinds
    if (matchesFolder(key, "components")) {
      if (key.endsWith(`.${codeBehindFileExtension}`)) {
        // --- "default" contains the functions and variables declared in the
        // --- component's code behind file.
        codeBehindsByFileName[key] = value.default;
        const componentCompilationForCodeBehind = projectCompilation.components.findLast(
          ({ filename }) => {
            const idxOfCodeBehindFileExtension = key.lastIndexOf(codeBehindFileExtension);
            const idxOfComponentFileExtension = filename.lastIndexOf(componentFileExtension);
            const extensionlessFilenamesMatch =
              filename.substring(0, idxOfComponentFileExtension) ===
              key.substring(0, idxOfCodeBehindFileExtension);

            return extensionlessFilenamesMatch;
          },
        );

        componentCompilationForCodeBehind.codeBehindSource = value.default.src;
      } else {
        // --- "default" contains the component definition, the file index,
        // --- and the source code.
        componentsByFileName[key] = value.default.component;
        sources[value.default.file] = value.default.src;

        const componentCompilation: ComponentCompilation = {
          definition: value.default.component,
          filename: key,
          markupSource: value.default.src,
          dependencies: new Set(),
        };
        projectCompilation.components.push(componentCompilation);
      }
    }

    // --- Collect the themes declared with the app
    if (matchesFolder(key, "themes")) {
      themes.push(value.default);
    }
  }

  // --- We have an entry point defined in the configuration file or in the main app file.
  const safeEntryPoint = config?.entryPoint || entryPoint;

  // --- We may have a code-behind file. If so, we merge the variables and functions
  const entryPointWithCodeBehind = {
    ...safeEntryPoint,
    vars: {
      ...entryPointCodeBehind?.vars,
      ...safeEntryPoint?.vars,
    },
    functions: entryPointCodeBehind?.functions,
    scriptError: entryPointCodeBehind?.moduleErrors,
  } as ComponentLike;

  // --- Collect the component definition we pass to the rendering engine
  let components: Array<CompoundComponentDef> = [];
  if (config?.components) {
    // --- We have a list of components defined in the configuration file
    components = config.components;
  } else {
    // --- Use the components collected from the runtime files; merge the components
    // --- with their code behinds
    Object.entries(componentsByFileName).forEach(([key, compound]) => {
      const componentCodeBehind = codeBehindsByFileName[`${key}.xs`];
      const componentWithCodeBehind = {
        ...compound,
        component: {
          ...compound.component,
          vars: {
            ...compound.component.vars,
            ...componentCodeBehind?.vars,
          },
          functions: componentCodeBehind?.functions,
          scriptError: componentCodeBehind?.moduleErrors,
        },
      };
      components.push(componentWithCodeBehind);
    });
  }

  // --- Done.
  return {
    standaloneApp: {
      ...config,
      entryPoint: entryPointWithCodeBehind,
      components,
      themes: config?.themes || themes,
      apiInterceptor: config?.apiInterceptor || apiInterceptor,
      sources,
    },
    projectCompilation,
  };
}

/**
 * Merges app definitions
 * @param resolvedRuntime Standalone app definition coming from the resolved runtime
 * @param standaloneAppDef Standalode app definition coming from the source
 * @returns Merged standalone app definition
 */
function mergeAppDefWithRuntime(
  resolvedRuntime: StandaloneAppDescription,
  standaloneAppDef: StandaloneAppDescription | undefined,
): StandaloneAppDescription {
  if (!standaloneAppDef) {
    return resolvedRuntime;
  }
  return {
    ...standaloneAppDef,
    entryPoint: standaloneAppDef.entryPoint || resolvedRuntime.entryPoint,
    components: standaloneAppDef.components || resolvedRuntime.components,
    themes: standaloneAppDef.themes || resolvedRuntime.themes,
    apiInterceptor: standaloneAppDef.apiInterceptor || resolvedRuntime.apiInterceptor,
  };
}

function resolvePath(basePath: string, relativePath: string) {
  // Create a base URL. The 'http://dummy.com' is just a placeholder.
  const baseUrl = new URL(basePath, "http://dummy.com");

  // Create a new URL by resolving the relative path against the base URL.
  const resolvedUrl = new URL(relativePath, baseUrl);

  // Return the pathname, removing the leading slash.
  return resolvedUrl.pathname.substring(1);
}

/**
 * Helper function to load theme files with support for both JSON and YAML formats.
 * First tries to load as JSON, if that fails, attempts to load as YAML.
 * @param url The URL to fetch the theme from
 * @returns A Promise resolving to the parsed theme definition
 */
async function loadThemeFile(url: string): Promise<ThemeDefinition> {
  // First try to load as JSON
  try {
    const response = await fetchWithoutCache(url);
    if (!response.ok) {
      // If the JSON file doesn't exist, try YAML immediately
      throw new Error(`Failed to fetch ${url}`);
    }

    // Get the content as text first
    const text = await response.text();

    // Try to parse as JSON
    try {
      return JSON.parse(text);
    } catch (jsonParseError) {
      // If JSON parsing fails, it might be a YAML file with a .json extension
      // or we need to try the .yml version
      console.warn(`Failed to parse ${url} as JSON, attempting YAML parsing.`);
      try {
        return yaml.load(text) as ThemeDefinition;
      } catch (yamlParseError) {
        // If both JSON and YAML parsing fail for this file, try the .yml version
        throw jsonParseError;
      }
    }
  } catch (jsonError) {
    // If JSON file loading fails, try YAML
    const yamlUrl = url.replace(/\.json$/, ".yml");
    try {
      const response = await fetchWithoutCache(yamlUrl);
      if (!response.ok) throw new Error(`Failed to fetch ${yamlUrl}`);
      const text = await response.text();
      return yaml.load(text) as ThemeDefinition;
    } catch (yamlError) {
      console.error(
        `Failed to load theme file: ${url} (JSON error:`,
        jsonError,
        "YAML error:",
        yamlError,
        ")",
      );
      throw new Error(`Failed to load theme file ${url} in either JSON or YAML format`);
    }
  }
}

/**
 * Fetch the up-to-date state of the source file
 * @param url The URL to fetch the source file from
 * @returns The source file contents response
 */
function fetchWithoutCache(url: string): Promise<Response> {
  return fetch(normalizePath(url), {
    headers: {
      "Cache-Control": "no-cache, no-store",
    },
  });
}

/**
 * Using its definition, this React hook prepares the runtime environment of a
 * standalone app. It runs every time an app source file changes.
 * @param standaloneAppDef The standalone app description
 * @param runtime The pre-compiled runtime environment
 * @returns The prepared StandaloneAppDescription if the collection is
 * successful; otherwise, null.
 */
function useStandalone(
  standaloneAppDef: StandaloneAppDescription | undefined,
  runtime: Record<string, any> = EMPTY_OBJECT,
  extensionManager?: StandaloneExtensionManager,
): { standaloneApp: StandaloneAppDescription | null; projectCompilation?: ProjectCompilation } {
  const [standaloneApp, setStandaloneApp] = useState<StandaloneAppDescription | null>(() => {
    // --- Initialize the standalone app
    const resolvedRuntime = resolveRuntime(runtime);
    const appDef = mergeAppDefWithRuntime(resolvedRuntime.standaloneApp, standaloneAppDef);

    // --- In dev mode or when the app is inlined (provided we do not use the standalone mode),
    // --- we must have the app definition available.
    if (
      (process.env.VITE_DEV_MODE || process.env.VITE_BUILD_MODE === "INLINE_ALL") &&
      !process.env.VITE_STANDALONE
    ) {
      if (!appDef) {
        throw new Error("couldn't find the application metadata");
      }
      return appDef;
    }

    // --- No standalone app yet, we need to get that from the fetched source code
    return null;
  });

  const [projectCompilation, setProjectCompilation] = useState<ProjectCompilation>(null);

  useIsomorphicLayoutEffect(() => {
    void (async function () {
      const resolvedRuntime = resolveRuntime(runtime);
      const appDef = mergeAppDefWithRuntime(resolvedRuntime.standaloneApp, standaloneAppDef);

      // --- In dev mode or when the app is inlined (provided we do not use the standalone mode),
      // --- we must have the app definition available.
      if (
        (process.env.VITE_DEV_MODE || process.env.VITE_BUILD_MODE === "INLINE_ALL") &&
        !process.env.VITE_STANDALONE
      ) {
        if (!appDef) {
          throw new Error("couldn't find the application metadata");
        }
        const lintErrorComponent = processAppLinting(appDef, metadataProvider);
        if (lintErrorComponent) {
          appDef.entryPoint = lintErrorComponent;
        }

        discoverCompilationDependencies({
          projectCompilation: resolvedRuntime.projectCompilation,
          extensionManager,
        });
        setProjectCompilation(resolvedRuntime.projectCompilation);
        setStandaloneApp(appDef);
        return;
      }

      // --- In standalone mode, we must fetch the XMLUI app's source files,
      // --- compile them, and prepare the app's definition for the rendering
      // --- engine.
      if (process.env.VITE_BUILD_MODE === "CONFIG_ONLY") {
        // --- In config-only mode, we override the pre-compiled app definition
        // --- with elements from the configuration file. Note that we do not
        // --- check whether the config file's content is semantically valid.
        const configResponse = await fetchWithoutCache(CONFIG_FILE);
        const config: StandaloneJsonConfig = await configResponse.json();

        const themePromises: Promise<ThemeDefinition>[] = [];
        config.themes?.forEach((theme) => {
          themePromises.push(loadThemeFile(theme));
        });
        const themes = await Promise.all(themePromises);

        const newAppDef = {
          ...appDef,
          name: config.name,
          appGlobals: config.appGlobals,
          defaultTheme: config.defaultTheme,
          resources: config.resources,
          resourceMap: config.resourceMap,
          themes,
        };

        const lintErrorComponent = processAppLinting(newAppDef, metadataProvider);
        if (lintErrorComponent) {
          newAppDef.entryPoint = lintErrorComponent;
        }
        discoverCompilationDependencies({
          projectCompilation: resolvedRuntime.projectCompilation,
          extensionManager,
        });
        setProjectCompilation(resolvedRuntime.projectCompilation);
        setStandaloneApp(newAppDef);
        return;
      }

      // --- Fetch the main file
      const entryPointPromise = new Promise(async (resolve) => {
        try {
          const resp = await fetchWithoutCache(MAIN_FILE);
          if (resp.ok) {
            resolve(parseComponentMarkupResponse(resp));
          } else {
            resolve({
              component: errReportMessage(`Failed to load the main component (${MAIN_FILE})`),
              file: MAIN_FILE,
              hasError: true,
            });
          }
        } catch (e) {
          resolve(null);
        }
      }) as any;

      // --- Fetch the configuration file (we do not check whether the content is semantically valid)
      let config: StandaloneJsonConfig = undefined;
      try {
        const configResponse = await fetchWithoutCache(CONFIG_FILE);
        config = await configResponse.json();
      } catch (e) {}

      // --- Fetch the themes according to the configuration
      let themePromises: Promise<ThemeDefinition>[];
      if ((config?.themes ?? []).length === 0 && config?.defaultTheme) {
        // --- Special case, we have only a single "defaultTheme" in the configuration
        const fetchDefaultTheme = loadThemeFile(`themes/${config?.defaultTheme}.json`);
        themePromises = [fetchDefaultTheme];
      } else {
        // --- In any other case, we fetch all themes defined in the configuration
        themePromises = config?.themes?.map((themePath) => {
          return loadThemeFile(themePath);
        });
      }
      // --- Fetch component files according to the configuration
      const componentPromises = config?.components?.map(async (componentPath) => {
        const value = await fetchWithoutCache(componentPath);
        if (componentPath.endsWith(`.${componentFileExtension}`)) {
          return await parseComponentMarkupResponse(value);
        } else {
          return await parseCodeBehindResponse(value);
        }
      });

      // --- Let the promises resolve
      const [loadedEntryPoint, loadedComponents, themes] = await Promise.all([
        entryPointPromise,
        Promise.all(componentPromises || []),
        Promise.all(themePromises || []),
      ]);

      // --- Collect the elements of the standalone app (and potential errors)
      const errorComponents: ComponentDef[] = [];

      // --- Check if the main component has errors
      if (loadedEntryPoint.hasError) {
        errorComponents.push(loadedEntryPoint!.component as ComponentDef);
      }

      let loadedEntryPointCodeBehind = null;
      if (loadedEntryPoint.component.props?.codeBehind !== undefined) {
        // --- We have a code-behind file for the main component
        loadedEntryPointCodeBehind = (await new Promise(async (resolve) => {
          try {
            const resp = await fetchWithoutCache(
              resolvePath(
                MAIN_FILE,
                loadedEntryPoint.component.props?.codeBehind || MAIN_CODE_BEHIND_FILE,
              ),
            );
            const codeBehind = await parseCodeBehindResponse(resp);
            resolve(codeBehind.hasError ? codeBehind : codeBehind.codeBehind);
          } catch (e) {
            resolve(null);
          }
        })) as any;
        if (loadedEntryPointCodeBehind.hasError) {
          errorComponents.push(loadedEntryPointCodeBehind.component as ComponentDef);
        }
      }

      // --- Check if any of the components have markup errors
      loadedComponents.forEach((compWrapper) => {
        if (compWrapper.hasError) {
          errorComponents.push(compWrapper.component as ComponentDef);
        }
      });

      // --- Collect the sources and code-behinds
      const sources: Record<string, string> = {};
      const codeBehinds: any = {};

      // --- The main component source
      if (loadedEntryPoint.file) {
        sources[loadedEntryPoint.file] = loadedEntryPoint.src;

        resolvedRuntime.projectCompilation.entrypoint.filename = MAIN_FILE;
        resolvedRuntime.projectCompilation.entrypoint.definition = loadedEntryPoint.component;
        resolvedRuntime.projectCompilation.entrypoint.markupSource = loadedEntryPoint.src;

        if (loadedEntryPointCodeBehind?.src !== undefined) {
          resolvedRuntime.projectCompilation.entrypoint.codeBehindSource =
            loadedEntryPointCodeBehind.src;
        }
      }

      // --- Components may have code-behind files
      loadedComponents.forEach((compWrapper) => {
        if (compWrapper?.file?.endsWith(`.${codeBehindFileExtension}`)) {
          codeBehinds[compWrapper.file] = compWrapper.codeBehind;
        } else {
          if (compWrapper.file) {
            sources[compWrapper.file] = compWrapper.src;
            // resolvedRuntime.projectCompilation.entrypoint.filename = MAIN_FILE;
            // resolvedRuntime.projectCompilation.entrypoint.definition = loadedEntryPoint.component;
            // resolvedRuntime.projectCompilation.entrypoint.markupSource = loadedEntryPoint.src;
          }
        }
      });

      // --- Assemble the runtime for the main app file
      const entryPointWithCodeBehind = {
        ...loadedEntryPoint.component,
        vars: {
          ...loadedEntryPointCodeBehind?.vars,
          ...loadedEntryPoint.component.vars,
        },
        functions: loadedEntryPointCodeBehind?.functions,
        scriptError: loadedEntryPointCodeBehind?.moduleErrors,
      };

      const defaultTheme = (entryPointWithCodeBehind as ComponentDef).props?.defaultTheme;
      // --- We test whether the default theme is not from a binding
      // --- expression and is not a built-in theme already loaded. If so,
      // --- we load it from the `themes` folder.
      if (defaultTheme && typeof defaultTheme === "string" && !defaultTheme.includes("{")) {
        if (
          !builtInThemes.find((theme) => theme.id === defaultTheme) &&
          !themes.find((theme) => theme.id === defaultTheme)
        ) {
          themes.push(await loadThemeFile(`themes/${defaultTheme}.json`));
        }
      }

      // --- Assemble the runtime for the components
      const componentsWithCodeBehinds = loadedComponents
        .filter((compWrapper) => !compWrapper?.file?.endsWith(".xmlui.xs"))
        .map((compWrapper) => {
          const componentCodeBehind = codeBehinds[compWrapper.file + ".xs"];
          return {
            ...compWrapper.component,
            component: {
              ...(compWrapper.component as any).component,
              vars: {
                ...(compWrapper.component as any).component.vars,
                ...componentCodeBehind?.vars,
              },
              functions: componentCodeBehind?.functions,
              scriptError: componentCodeBehind?.moduleErrors,
            },
          };
        });

      // --- We may have components that are not in the configuration file.
      // --- We need to load them and their code-behinds. First, we collect
      // --- the components to load.
      let componentsToLoad = collectMissingComponents(
        entryPointWithCodeBehind,
        componentsWithCodeBehinds,
        undefined,
        extensionManager,
      );

      // --- Try to load the components referenced in the markup, collect
      // --- those that failed
      const componentsFailedToLoad = new Set();
      while (componentsToLoad.size > 0) {
        const componentPromises = [...componentsToLoad].map(async (componentPath) => {
          try {
            // --- Promises for the component markup files
            const componentPromise = fetchWithoutCache(
              `components/${componentPath}.${componentFileExtension}`,
            );

            // --- Let the promises resolve
            const componentMarkup = await componentPromise;

            // --- Parse the component markup and check for errors
            const compWrapper = await parseComponentMarkupResponse(componentMarkup);
            if (compWrapper.hasError) {
              errorComponents.push(compWrapper.component as ComponentDef);
            }

            sources[compWrapper.file] = compWrapper.src;
            const compCompilation: ComponentCompilation = {
              dependencies: new Set(),
              filename: compWrapper.file,
              markupSource: compWrapper.src,
              definition: compWrapper.component as CompoundComponentDef,
            };

            let componentCodeBehind = null;
            if (
              "codeBehind" in compWrapper.component &&
              compWrapper.component?.codeBehind !== undefined
            ) {
              // --- Promises for the component code-behind files
              componentCodeBehind = (await new Promise(async (resolve) => {
                try {
                  const codeBehind = await fetchWithoutCache(
                    resolvePath(
                      `components/${componentPath}`,
                      (compWrapper.component as CompoundComponentDef)?.codeBehind ||
                        `${componentPath}.${codeBehindFileExtension}`,
                    ),
                  );
                  const codeBehindWrapper = await parseCodeBehindResponse(codeBehind);
                  if (codeBehindWrapper.hasError) {
                    errorComponents.push(codeBehindWrapper.component as ComponentDef);
                  }
                  resolve(
                    codeBehindWrapper.hasError
                      ? (codeBehindWrapper.component as CompoundComponentDef)
                      : codeBehindWrapper,
                  );
                } catch {
                  resolve(null);
                }
              })) as Promise<CompoundComponentDef | ParsedResponse>;

              if (componentCodeBehind && "src" in componentCodeBehind) {
                compCompilation.codeBehindSource = componentCodeBehind.src;
              }
            }

            resolvedRuntime.projectCompilation.components.push(compCompilation);

            const compoundComp = {
              ...compWrapper.component,
              component: {
                ...(compWrapper.component as any).component,
                vars: {
                  ...(compWrapper.component as any).component.vars,
                  ...componentCodeBehind?.codeBehind?.vars,
                },
              },
            };

            if (componentCodeBehind && "codeBehind" in componentCodeBehind) {
              compoundComp.component.functions = componentCodeBehind.codeBehind.functions;
              compoundComp.component.scriptError = componentCodeBehind.codeBehind.moduleErrors;
            }

            return compoundComp;
          } catch (e) {
            componentsFailedToLoad.add(componentPath);
            return null;
          }
        });
        const componentWrappers = await Promise.all(componentPromises);
        componentsWithCodeBehinds.push(...componentWrappers.filter((comp) => !!comp));
        componentsToLoad = collectMissingComponents(
          entryPointWithCodeBehind,
          componentsWithCodeBehinds,
          componentsFailedToLoad,
          extensionManager,
        );
      }
      // --- Let's check for errors to display

      const newAppDef = {
        ...config,
        themes,
        sources,
        components: componentsWithCodeBehinds as any,
        entryPoint: entryPointWithCodeBehind,
      };

      const lintErrorComponent = processAppLinting(newAppDef, metadataProvider);

      const errorComponent: ComponentDef | null =
        errorComponents.length > 0
          ? {
              type: "VStack",
              props: { gap: 0, padding: 0 },
              children: errorComponents,
            }
          : null;
      if (errorComponent) {
        if (lintErrorComponent) {
          errorComponent.children!.push(lintErrorComponent);
        }
        newAppDef.entryPoint = errorComponent;
      } else if (lintErrorComponent) {
        newAppDef.entryPoint = lintErrorComponent;
      }

      discoverCompilationDependencies({
        projectCompilation: resolvedRuntime.projectCompilation,
        extensionManager,
      });

      setProjectCompilation(resolvedRuntime.projectCompilation);
      setStandaloneApp(newAppDef);
    })();
  }, [runtime, standaloneAppDef]);
  return { standaloneApp, projectCompilation };
}

/**
 * Collect the missing components referenced by any part of the app
 * @param entryPoint The app's main markup
 * @param components The component markups
 * @param componentsFailedToLoad The components that failed to load here
 * @returns The components that are still missing
 */
function collectMissingComponents(
  entryPoint: ComponentDef | CompoundComponentDef,
  components: any[],
  componentsFailedToLoad = new Set(),
  extensionManager?: StandaloneExtensionManager,
) {
  // --- Add the discovered compound components to the registry
  const componentRegistry = new ComponentRegistry(
    { compoundComponents: components },
    extensionManager,
  );

  // --- Check the xmlui markup. This check will find all unloaded components
  const result = checkXmlUiMarkup(entryPoint as ComponentDef, components, {
    getComponentProps: (componentName) => {
      return componentRegistry.lookupComponentRenderer(componentName)?.descriptor?.props;
    },
    acceptArbitraryProps: () => {
      return true;
    },
    getComponentValidator: () => {
      return null;
    },
    getComponentEvents: () => {
      return null;
    },
    componentRegistered: (componentName) => {
      return componentRegistry.hasComponent(componentName);
    },
  });

  componentRegistry.destroy();

  // --- Collect all missing components.
  // Omit the components that failed to load and the ones that are not in #app-ns namespace
  return new Set(
    result
      .filter((r) => r.code === "M001")
      .map((r) => r.args[0].replace("#app-ns.", ""))
      .filter((comp) => !componentsFailedToLoad.has(comp) && !comp.includes(".")),
  );
}

// --- This React hook logs the app's version number to the browser's console at startup
function usePrintVersionNumber(standaloneApp: StandaloneAppDescription | null) {
  const logged = useRef(false);
  useEffect(() => {
    if (logged.current) {
      return;
    }
    logged.current = true;
    let log = `XMLUI version: ${process.env.VITE_XMLUI_VERSION || "dev"}`;
    if (standaloneApp?.name) {
      log += `; ${standaloneApp.name} version: ${process.env.VITE_APP_VERSION || "dev"}`;
    }
    console.log(log);
  }, [standaloneApp?.name]);
}

let contentRoot: Root | null = null;

/**
 * This function injects the StandaloneApp component into a React app. It looks
 * up a component with the "root" id as the host of the standalone app. If such
 * an element does not exist, it creates a `<div id="root">` element.
 * @param runtime The app's runtime representation
 * @param components The related component's runtime representation
 * @returns The content's root element
 */

export function startApp(
  runtime: any,
  extensions: Extension[] | Extension = [],
  extensionManager: StandaloneExtensionManager = new StandaloneExtensionManager(),
) {
  extensionManager.registerExtension(extensions);
  let rootElement: HTMLElement | null = document.getElementById("root");
  if (!rootElement) {
    rootElement = document.createElement("div");
    rootElement.setAttribute("id", "root");
    document.body.appendChild(rootElement);
  }
  if (!contentRoot) {
    contentRoot = ReactDOM.createRoot(rootElement);
  }
  contentRoot.render(<StandaloneApp runtime={runtime} extensionManager={extensionManager} />);
  return contentRoot;
}

export default StandaloneApp;

function processAppLinting(
  appDef: StandaloneAppDescription,
  metadataProvider: MetadataProvider,
): null | ComponentDef {
  const lintSeverity = getLintSeverity(appDef.appGlobals?.lintSeverity);

  if (lintSeverity !== LintSeverity.Skip) {
    const allComponentLints = lintApp({
      appDef,
      metadataProvider,
    });

    if (allComponentLints.length > 0) {
      if (lintSeverity === LintSeverity.Warning) {
        allComponentLints.forEach(printComponentLints);
      } else if (lintSeverity === LintSeverity.Error) {
        return lintErrorsComponent(allComponentLints);
      }
    }
    return null;
  }
}

function discoverCompilationDependencies({
  projectCompilation: { components, entrypoint },
  extensionManager,
}: {
  projectCompilation: ProjectCompilation;
  extensionManager?: StandaloneExtensionManager;
}) {
  const registry = new ComponentRegistry({}, extensionManager);

  const entrypointDependencies = discoverDirectComponentDependencies(
    entrypoint.definition,
    registry,
  );
  entrypoint.dependencies = entrypointDependencies;

  for (const componentCompilation of components) {
    const compDependencies = discoverDirectComponentDependencies(
      componentCompilation.definition.component,
      registry,
    );
    compDependencies.delete(componentCompilation.definition.name);
    componentCompilation.dependencies = compDependencies;
  }

  registry.destroy();
}

function discoverDirectComponentDependencies(
  entrypoint: ComponentDef,
  registry: ComponentRegistry,
): Set<string> {
  return discoverDirectComponentDependenciesHelp(entrypoint, registry, new Set<string>());
}

function discoverDirectComponentDependenciesHelp(
  component: ComponentDef,
  registry: ComponentRegistry,
  deps: Set<string>,
): Set<string> {
  if (!component) {
    return deps;
  }
  const compName = component.type;
  if (!registry.hasComponent(compName)) {
    deps.add(compName);
  }
  if (!component.children) {
    return deps;
  }

  for (const child of component.children) {
    discoverDirectComponentDependenciesHelp(child, registry, deps);
  }

  return deps;
}
```
Page 103/141FirstPrevNextLast