This is page 138 of 181. Use http://codebase.md/xmlui-org/xmlui/tools/vscode/resources/%7BextractResourceUrl(node.props.src)%7D?lines=true&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
--------------------------------------------------------------------------------
/xmlui/src/components-core/StandaloneApp.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import type { ReactNode } from "react";
2 | import { useEffect, useMemo, useRef, useState } from "react";
3 | import type { Root } from "react-dom/client";
4 | import ReactDOM from "react-dom/client";
5 | import yaml from "js-yaml";
6 |
7 | import type { StandaloneAppDescription, StandaloneJsonConfig } from "./abstractions/standalone";
8 | import type {
9 | ComponentDef,
10 | ComponentLike,
11 | CompoundComponentDef,
12 | } from "../abstractions/ComponentDefs";
13 |
14 | import "../index.scss";
15 | import { AppRoot } from "./rendering/AppRoot";
16 | import { normalizePath } from "./utils/misc";
17 | import { ApiInterceptorProvider } from "./interception/ApiInterceptorProvider";
18 | import { EMPTY_OBJECT } from "./constants";
19 | import {
20 | errReportComponent,
21 | errReportMessage,
22 | errReportModuleErrors,
23 | errReportScriptError,
24 | xmlUiMarkupToComponent,
25 | } from "./xmlui-parser";
26 | import { useIsomorphicLayoutEffect } from "./utils/hooks";
27 | import {
28 | codeBehindFileExtension,
29 | componentFileExtension,
30 | } from "../parsers/xmlui-parser/fileExtensions";
31 | import { Parser } from "../parsers/scripting/Parser";
32 | import {
33 | collectCodeBehindFromSource,
34 | removeCodeBehindTokensFromTree,
35 | } from "../parsers/scripting/code-behind-collect";
36 | import { ComponentRegistry } from "../components/ComponentProvider";
37 | import { checkXmlUiMarkup } from "./markup-check";
38 | import StandaloneExtensionManager from "./StandaloneExtensionManager";
39 | import { builtInThemes } from "./theming/ThemeProvider";
40 | import type { Extension } from "../abstractions/ExtensionDefs";
41 | import {
42 | getLintSeverity,
43 | lintApp,
44 | lintErrorsComponent,
45 | LintSeverity,
46 | printComponentLints,
47 | } from "../parsers/xmlui-parser/lint";
48 | import { collectedComponentMetadata } from "../components/collectedComponentMetadata";
49 | import type { ThemeDefinition, ThemeTone } from "../abstractions/ThemingDefs";
50 | import type {
51 | ComponentCompilation,
52 | ProjectCompilation,
53 | } from "../abstractions/scripting/Compilation";
54 | import { MetadataProvider } from "../language-server/services/common/metadata-utils";
55 | import type { CollectedDeclarations } from "./script-runner/ScriptingSourceTree";
56 |
57 | const MAIN_FILE = "Main." + componentFileExtension;
58 | const MAIN_CODE_BEHIND_FILE = "Main." + codeBehindFileExtension;
59 | const CONFIG_FILE = "config.json";
60 |
61 | const metadataProvider = new MetadataProvider(collectedComponentMetadata);
62 |
63 | type RuntimeProps = {
64 | default?: any;
65 | component?: ComponentDef | CompoundComponentDef;
66 | file?: string;
67 | src?: string;
68 | }
69 |
70 | // --- The properties of the standalone app
71 | type StandaloneAppProps = {
72 | // --- The standalone app description (the engine renders this definition)
73 | appDef?: StandaloneAppDescription;
74 | appGlobals?: Record<string, any>;
75 |
76 | // --- In E2E tests, we can decorate the components with test IDs
77 | decorateComponentsWithTestId?: boolean;
78 |
79 | // --- Debugging can be enabled or disabled
80 | debugEnabled?: boolean;
81 |
82 | // --- The runtime environment of the standalone app (for pre-compiled apps)
83 | runtime?: RuntimeProps;
84 |
85 | // --- The object responsible for managing the standalone components
86 | extensionManager?: StandaloneExtensionManager;
87 |
88 | // --- If true, the app waits for the API interceptor to be ready
89 | waitForApiInterceptor?: boolean;
90 | children?: ReactNode;
91 | };
92 |
93 | /**
94 | * This React component represents a standalone app that implements a web
95 | * application with xmlui components. A StandaloneApp instance uses a
96 | * AppRoot wrapped into an ApiInterceptor.
97 | *
98 | * AppRoot is responsible for rendering the app (using an internal
99 | * representation); ApiInterceptor can emulate some backend functionality
100 | * running in the browser.
101 | */
102 | function StandaloneApp({
103 | appDef,
104 | appGlobals: globals,
105 | decorateComponentsWithTestId,
106 | debugEnabled = false,
107 | runtime,
108 | extensionManager,
109 | waitForApiInterceptor = false,
110 | children,
111 | }: StandaloneAppProps) {
112 | // --- Fetch all files constituting the standalone app, including components,
113 | // --- themes, and other artifacts. Display the app version numbers in the
114 | // --- console.
115 | const { standaloneApp, projectCompilation } = useStandalone(appDef, runtime, extensionManager);
116 |
117 | usePrintVersionNumber(standaloneApp);
118 |
119 | const {
120 | apiInterceptor,
121 | name,
122 | appGlobals,
123 | defaultTheme,
124 | defaultTone,
125 | resources,
126 | resourceMap,
127 | entryPoint,
128 | components,
129 | themes,
130 | sources,
131 | } = standaloneApp || {};
132 |
133 | const globalProps = useMemo(() => {
134 | return {
135 | name: name,
136 | ...(appGlobals || {}),
137 | ...(globals || {}),
138 | };
139 | }, [appGlobals, globals, name]);
140 |
141 | let contributes = useMemo(() => {
142 | return {
143 | compoundComponents: components,
144 | themes,
145 | };
146 | }, [components, themes]);
147 |
148 | if (!standaloneApp) {
149 | // --- Problems found, the standalone app cannot run
150 | return null;
151 | }
152 |
153 | // --- The app may use a mocked API already defined in `window.XMLUI_MOCK_API`
154 | // --- or within the standalone app's definition, in `apiInterceptor`.
155 | const mockedApi =
156 | // @ts-ignore
157 | typeof window !== "undefined" && window.XMLUI_MOCK_API ? window.XMLUI_MOCK_API : apiInterceptor;
158 |
159 | // --- Components can be decorated with test IDs used in end-to-end tests.
160 | // --- This flag checks the environment if the app runs in E2E test mode.
161 | const shouldDecorateWithTestId =
162 | decorateComponentsWithTestId ||
163 | // @ts-ignore
164 | (typeof window !== "undefined" ? window.XMLUI_MOCK_TEST_ID : false);
165 |
166 | // --- An app can turn off the default hash routing.
167 | const useHashBasedRouting = appGlobals?.useHashBasedRouting ?? true;
168 |
169 | return (
170 | <ApiInterceptorProvider
171 | interceptor={mockedApi}
172 | useHashBasedRouting={useHashBasedRouting}
173 | waitForApiInterceptor={waitForApiInterceptor}
174 | >
175 | <AppRoot
176 | projectCompilation={projectCompilation}
177 | decorateComponentsWithTestId={shouldDecorateWithTestId}
178 | node={entryPoint!}
179 | standalone={true}
180 | debugEnabled={debugEnabled}
181 | // @ts-ignore
182 | routerBaseName={typeof window !== "undefined" ? window.__PUBLIC_PATH || "" : ""}
183 | globalProps={globalProps}
184 | defaultTheme={defaultTheme}
185 | defaultTone={defaultTone as ThemeTone}
186 | resources={resources}
187 | resourceMap={resourceMap}
188 | sources={sources}
189 | extensionManager={extensionManager}
190 | contributes={contributes}
191 | >
192 | {children}
193 | </AppRoot>
194 | </ApiInterceptorProvider>
195 | );
196 | }
197 |
198 | // --- This type represents the parsed structure of a component markup of
199 | // --- code-behind file (for further processing)
200 | type ParsedResponse = {
201 | // --- The component definition (it may be a compound component)
202 | component?: ComponentDef | CompoundComponentDef;
203 |
204 | // --- The optional code-behind source code of the component
205 | codeBehind?: CollectedDeclarations;
206 |
207 | // --- The optional source code of the component (for debugging or learning purposes)
208 | src?: string;
209 |
210 | // --- The optional file name of the component (for debugging or learning purposes)
211 | file?: string;
212 |
213 | // --- The flag indicating if the component has errors
214 | hasError?: boolean;
215 | };
216 |
217 | /**
218 | * This function parses the response of a fetch retrieving the contents of a
219 | * component markup file.
220 | * @param response The response coming from the fetch
221 | * @returns If parsing is successful, it returns the parsed response containing
222 | * the component definition. Otherwise, it returns a component definition that
223 | * displays the errors.
224 | */
225 | async function parseComponentMarkupResponse(response: Response): Promise<ParsedResponse> {
226 | if (!response.ok) {
227 | throw new Error(`Failed to fetch ${response.url}`);
228 | }
229 | const code = await response.text();
230 | const fileId = response.url;
231 | let { component, errors, erroneousCompoundComponentName } = xmlUiMarkupToComponent(code, fileId);
232 | if (errors.length > 0) {
233 | const compName =
234 | erroneousCompoundComponentName ??
235 | response.url.substring(
236 | response.url.lastIndexOf("/") + 1,
237 | response.url.length - ".xmlui".length,
238 | );
239 | component = errReportComponent(errors, fileId, compName);
240 | }
241 | return {
242 | component,
243 | src: code,
244 | file: fileId,
245 | hasError: errors.length > 0,
246 | };
247 | }
248 |
249 | /**
250 | * This function parses the response of a fetch retrieving the contents of a
251 | * code-behind file.
252 | * @param response The response coming from the fetch
253 | * @returns If parsing is successful, it returns the parsed response containing
254 | * the code-behind declarations. Otherwise, it returns a component definition that
255 | * displays the errors.
256 | */
257 | async function parseCodeBehindResponse(response: Response): Promise<ParsedResponse> {
258 | if (!response.ok) {
259 | throw new Error(`Failed to fetch ${response.url}`);
260 | }
261 | const code = await response.text();
262 | const parser = new Parser(code);
263 | try {
264 | parser.parseStatements();
265 | } catch (e) {
266 | console.error("Error parsing code behind", e);
267 | // throw new Error(`Failed to fetch ${response.url}`);
268 | if (parser.errors.length > 0) {
269 | return {
270 | component: errReportScriptError(parser.errors[0], response.url),
271 | file: response.url,
272 | hasError: true,
273 | };
274 | }
275 | }
276 |
277 | try {
278 | const codeBehind = collectCodeBehindFromSource("Main", code);
279 | if (Object.keys(codeBehind.moduleErrors ?? {}).length > 0) {
280 | return {
281 | component: errReportModuleErrors(codeBehind.moduleErrors, response.url),
282 | file: response.url,
283 | hasError: true,
284 | };
285 | }
286 |
287 | // --- Remove the code-behind tokens from the tree shrinking the tree
288 | removeCodeBehindTokensFromTree(codeBehind);
289 | return {
290 | src: code,
291 | codeBehind: codeBehind,
292 | file: response.url,
293 | };
294 | } catch (e) {
295 | console.error("Error collecting code behind", e);
296 | }
297 | }
298 |
299 | // --- Tests is the given path matches the speified file name
300 | function matchesFileName(path: string, fileName: string) {
301 | return (
302 | path.endsWith(`/${fileName}.ts`) ||
303 | path.endsWith(`/${fileName}.js`) ||
304 | path.endsWith(`/${fileName}.${componentFileExtension}`) ||
305 | path.endsWith(`/${fileName}.${codeBehindFileExtension}`)
306 | );
307 | }
308 |
309 | // --- Tests if the given path contains the specified folder
310 | function matchesFolder(path: string, folderName: string) {
311 | return path.includes(`/${folderName}/`);
312 | }
313 |
314 | /**
315 | * This function turns a collection of runtime file declarations into a standalone
316 | * app description.
317 | * @param runtime A hash object containing the runtime files. The keys are the file
318 | * paths and the values are the file contents.
319 | * @returns The standalone app description.
320 | *
321 | * When the standalone app is pre-compiled, each property in `runtime` holds such a
322 | * pre-compiled item. Otherwise, `runtime` is an empty object.
323 | *
324 | * While processing the files here, we can assume they are free from compilation
325 | * errors, as such errors would be observed in the compile phase.
326 | */
327 | function resolveRuntime(runtime: Record<string, any>): {
328 | standaloneApp: StandaloneAppDescription;
329 | projectCompilation?: ProjectCompilation;
330 | } {
331 | // --- Collect the components and their sources. We pass the sources to the standalone app
332 | // --- so that it can be used for error display and debugging purposes.
333 | const projectCompilation: ProjectCompilation = {
334 | entrypoint: {
335 | filename: "",
336 | definition: null,
337 | dependencies: new Set(),
338 | },
339 | components: [],
340 | themes: {},
341 | };
342 | const sources: Record<string, string> = {};
343 | const componentsByFileName: Record<string, CompoundComponentDef> = {};
344 | const codeBehindsByFileName: Record<string, CollectedDeclarations> = {};
345 | const themes: Array<ThemeDefinition> = [];
346 |
347 | // --- Some working variables
348 | let config: StandaloneAppDescription | undefined;
349 | let entryPoint: ComponentDef | undefined;
350 | let entryPointCodeBehind: CollectedDeclarations | undefined;
351 | let apiInterceptor: any;
352 |
353 | // --- Process the runtime files
354 | for (const [key, value] of Object.entries(runtime)) {
355 | if (matchesFileName(key, "config")) {
356 | // --- We assume that the config file has a default export and this
357 | // --- export is the standalone app's configuration.
358 | config = value.default;
359 | }
360 |
361 | // --- We assume that the entry point is either named "Main" or "App".
362 | if (matchesFileName(key, "Main") || matchesFileName(key, "App")) {
363 | if (key.endsWith(codeBehindFileExtension)) {
364 | // --- "default" contains the functions and variables declared in the
365 | // --- code behind file.
366 | entryPointCodeBehind = value.default;
367 | projectCompilation.entrypoint.codeBehindSource = value.default.src;
368 | } else {
369 | projectCompilation.entrypoint.filename = key;
370 | // --- "default" contains the component definition, the file index,
371 | // --- and the source code.
372 | entryPoint = value.default.component;
373 | projectCompilation.entrypoint.definition = entryPoint;
374 | projectCompilation.entrypoint.markupSource = value.default.src;
375 | if (value.default.file) {
376 | // TODO: Remove this prop
377 | sources[value.default.file] = value.default.src;
378 | }
379 | }
380 | }
381 |
382 | // --- Use API emulation if available
383 | if (matchesFileName(key, "api")) {
384 | apiInterceptor = value.default;
385 | }
386 |
387 | // --- Collect the components and their code behinds
388 | if (matchesFolder(key, "components")) {
389 | if (key.endsWith(`.${codeBehindFileExtension}`)) {
390 | // --- "default" contains the functions and variables declared in the
391 | // --- component's code behind file.
392 | codeBehindsByFileName[key] = value.default;
393 | const componentCompilationForCodeBehind = projectCompilation.components.findLast(
394 | ({ filename }) => {
395 | const idxOfCodeBehindFileExtension = key.lastIndexOf(codeBehindFileExtension);
396 | const idxOfComponentFileExtension = filename.lastIndexOf(componentFileExtension);
397 | const extensionlessFilenamesMatch =
398 | filename.substring(0, idxOfComponentFileExtension) ===
399 | key.substring(0, idxOfCodeBehindFileExtension);
400 |
401 | return extensionlessFilenamesMatch;
402 | },
403 | );
404 |
405 | componentCompilationForCodeBehind.codeBehindSource = value.default.src;
406 | } else {
407 | // --- "default" contains the component definition, the file index,
408 | // --- and the source code.
409 | componentsByFileName[key] = value.default.component;
410 | sources[value.default.file] = value.default.src;
411 |
412 | const componentCompilation: ComponentCompilation = {
413 | definition: value.default.component,
414 | filename: key,
415 | markupSource: value.default.src,
416 | dependencies: new Set(),
417 | };
418 | projectCompilation.components.push(componentCompilation);
419 | }
420 | }
421 |
422 | // --- Collect the themes declared with the app
423 | if (matchesFolder(key, "themes")) {
424 | themes.push(value.default);
425 | }
426 | }
427 |
428 | // --- We have an entry point defined in the configuration file or in the main app file.
429 | const safeEntryPoint = config?.entryPoint || entryPoint;
430 |
431 | // --- We may have a code-behind file. If so, we merge the variables and functions
432 | const entryPointWithCodeBehind = {
433 | ...safeEntryPoint,
434 | vars: {
435 | ...entryPointCodeBehind?.vars,
436 | ...safeEntryPoint?.vars,
437 | },
438 | functions: entryPointCodeBehind?.functions,
439 | scriptError: entryPointCodeBehind?.moduleErrors,
440 | } as ComponentLike;
441 |
442 | // --- Collect the component definition we pass to the rendering engine
443 | let components: Array<CompoundComponentDef> = [];
444 | if (config?.components) {
445 | // --- We have a list of components defined in the configuration file
446 | components = config.components;
447 | } else {
448 | // --- Use the components collected from the runtime files; merge the components
449 | // --- with their code behinds
450 | Object.entries(componentsByFileName).forEach(([key, compound]) => {
451 | const componentCodeBehind = codeBehindsByFileName[`${key}.xs`];
452 | const componentWithCodeBehind = {
453 | ...compound,
454 | component: {
455 | ...compound.component,
456 | vars: {
457 | ...compound.component.vars,
458 | ...componentCodeBehind?.vars,
459 | },
460 | functions: componentCodeBehind?.functions,
461 | scriptError: componentCodeBehind?.moduleErrors,
462 | },
463 | };
464 | components.push(componentWithCodeBehind);
465 | });
466 | }
467 |
468 | // --- Done.
469 | return {
470 | standaloneApp: {
471 | ...config,
472 | entryPoint: entryPointWithCodeBehind,
473 | components,
474 | themes: config?.themes || themes,
475 | apiInterceptor: config?.apiInterceptor || apiInterceptor,
476 | sources,
477 | },
478 | projectCompilation,
479 | };
480 | }
481 |
482 | /**
483 | * Merges app definitions
484 | * @param resolvedRuntime Standalone app definition coming from the resolved runtime
485 | * @param standaloneAppDef Standalode app definition coming from the source
486 | * @returns Merged standalone app definition
487 | */
488 | function mergeAppDefWithRuntime(
489 | resolvedRuntime: StandaloneAppDescription,
490 | standaloneAppDef: StandaloneAppDescription | undefined,
491 | ): StandaloneAppDescription {
492 | if (!standaloneAppDef) {
493 | return resolvedRuntime;
494 | }
495 | return {
496 | ...standaloneAppDef,
497 | entryPoint: standaloneAppDef.entryPoint || resolvedRuntime.entryPoint,
498 | components: standaloneAppDef.components || resolvedRuntime.components,
499 | themes: standaloneAppDef.themes || resolvedRuntime.themes,
500 | apiInterceptor: standaloneAppDef.apiInterceptor || resolvedRuntime.apiInterceptor,
501 | };
502 | }
503 |
504 | function resolvePath(basePath: string, relativePath: string) {
505 | // Create a base URL. The 'http://dummy.com' is just a placeholder.
506 | const baseUrl = new URL(basePath, "http://dummy.com");
507 |
508 | // Create a new URL by resolving the relative path against the base URL.
509 | const resolvedUrl = new URL(relativePath, baseUrl);
510 |
511 | // Return the pathname, removing the leading slash.
512 | return resolvedUrl.pathname.substring(1);
513 | }
514 |
515 | /**
516 | * Helper function to load theme files with support for both JSON and YAML formats.
517 | * First tries to load as JSON, if that fails, attempts to load as YAML.
518 | * @param url The URL to fetch the theme from
519 | * @returns A Promise resolving to the parsed theme definition
520 | */
521 | async function loadThemeFile(url: string): Promise<ThemeDefinition> {
522 | // First try to load as JSON
523 | try {
524 | const response = await fetchWithoutCache(url);
525 | if (!response.ok) {
526 | // If the JSON file doesn't exist, try YAML immediately
527 | throw new Error(`Failed to fetch ${url}`);
528 | }
529 |
530 | // Get the content as text first
531 | const text = await response.text();
532 |
533 | // Try to parse as JSON
534 | try {
535 | return JSON.parse(text);
536 | } catch (jsonParseError) {
537 | // If JSON parsing fails, it might be a YAML file with a .json extension
538 | // or we need to try the .yml version
539 | console.warn(`Failed to parse ${url} as JSON, attempting YAML parsing.`);
540 | try {
541 | return yaml.load(text) as ThemeDefinition;
542 | } catch (yamlParseError) {
543 | // If both JSON and YAML parsing fail for this file, try the .yml version
544 | throw jsonParseError;
545 | }
546 | }
547 | } catch (jsonError) {
548 | // If JSON file loading fails, try YAML
549 | const yamlUrl = url.replace(/\.json$/, ".yml");
550 | try {
551 | const response = await fetchWithoutCache(yamlUrl);
552 | if (!response.ok) throw new Error(`Failed to fetch ${yamlUrl}`);
553 | const text = await response.text();
554 | return yaml.load(text) as ThemeDefinition;
555 | } catch (yamlError) {
556 | console.error(
557 | `Failed to load theme file: ${url} (JSON error:`,
558 | jsonError,
559 | "YAML error:",
560 | yamlError,
561 | ")",
562 | );
563 | throw new Error(`Failed to load theme file ${url} in either JSON or YAML format`);
564 | }
565 | }
566 | }
567 |
568 | /**
569 | * Fetch the up-to-date state of the source file
570 | * @param url The URL to fetch the source file from
571 | * @returns The source file contents response
572 | */
573 | function fetchWithoutCache(url: string): Promise<Response> {
574 | return fetch(normalizePath(url), {
575 | headers: {
576 | "Cache-Control": "no-cache, no-store",
577 | },
578 | });
579 | }
580 |
581 | /**
582 | * Using its definition, this React hook prepares the runtime environment of a
583 | * standalone app. It runs every time an app source file changes.
584 | * @param standaloneAppDef The standalone app description
585 | * @param runtime The pre-compiled runtime environment
586 | * @returns The prepared StandaloneAppDescription if the collection is
587 | * successful; otherwise, null.
588 | */
589 | function useStandalone(
590 | standaloneAppDef: StandaloneAppDescription | undefined,
591 | runtime: Record<string, any> = EMPTY_OBJECT,
592 | extensionManager?: StandaloneExtensionManager,
593 | ): { standaloneApp: StandaloneAppDescription | null; projectCompilation?: ProjectCompilation } {
594 | const [standaloneApp, setStandaloneApp] = useState<StandaloneAppDescription | null>(() => {
595 | // --- Initialize the standalone app
596 | const resolvedRuntime = resolveRuntime(runtime);
597 | const appDef = mergeAppDefWithRuntime(resolvedRuntime.standaloneApp, standaloneAppDef);
598 |
599 | // --- In dev mode or when the app is inlined (provided we do not use the standalone mode),
600 | // --- we must have the app definition available.
601 | if (
602 | (process.env.VITE_DEV_MODE || process.env.VITE_BUILD_MODE === "INLINE_ALL") &&
603 | !process.env.VITE_STANDALONE
604 | ) {
605 | if (!appDef) {
606 | throw new Error("couldn't find the application metadata");
607 | }
608 | return appDef;
609 | }
610 |
611 | // --- No standalone app yet, we need to get that from the fetched source code
612 | return null;
613 | });
614 |
615 | const [projectCompilation, setProjectCompilation] = useState<ProjectCompilation>(null);
616 |
617 | useIsomorphicLayoutEffect(() => {
618 | void (async function () {
619 | const resolvedRuntime = resolveRuntime(runtime);
620 | const appDef = mergeAppDefWithRuntime(resolvedRuntime.standaloneApp, standaloneAppDef);
621 |
622 | // --- In dev mode or when the app is inlined (provided we do not use the standalone mode),
623 | // --- we must have the app definition available.
624 | if (
625 | (process.env.VITE_DEV_MODE || process.env.VITE_BUILD_MODE === "INLINE_ALL") &&
626 | !process.env.VITE_STANDALONE
627 | ) {
628 | if (!appDef) {
629 | throw new Error("couldn't find the application metadata");
630 | }
631 | const lintErrorComponent = processAppLinting(appDef, metadataProvider);
632 | if (lintErrorComponent) {
633 | appDef.entryPoint = lintErrorComponent;
634 | }
635 |
636 | discoverCompilationDependencies({
637 | projectCompilation: resolvedRuntime.projectCompilation,
638 | extensionManager,
639 | });
640 | setProjectCompilation(resolvedRuntime.projectCompilation);
641 | setStandaloneApp(appDef);
642 | return;
643 | }
644 |
645 | // --- In standalone mode, we must fetch the XMLUI app's source files,
646 | // --- compile them, and prepare the app's definition for the rendering
647 | // --- engine.
648 | if (process.env.VITE_BUILD_MODE === "CONFIG_ONLY") {
649 | // --- In config-only mode, we override the pre-compiled app definition
650 | // --- with elements from the configuration file. Note that we do not
651 | // --- check whether the config file's content is semantically valid.
652 | const configResponse = await fetchWithoutCache(CONFIG_FILE);
653 | const config: StandaloneJsonConfig = await configResponse.json();
654 |
655 | const themePromises: Promise<ThemeDefinition>[] = [];
656 | config.themes?.forEach((theme) => {
657 | themePromises.push(loadThemeFile(theme));
658 | });
659 | const themes = await Promise.all(themePromises);
660 |
661 | const newAppDef = {
662 | ...appDef,
663 | name: config.name,
664 | appGlobals: config.appGlobals,
665 | defaultTheme: config.defaultTheme,
666 | resources: config.resources,
667 | resourceMap: config.resourceMap,
668 | themes,
669 | };
670 |
671 | const lintErrorComponent = processAppLinting(newAppDef, metadataProvider);
672 | if (lintErrorComponent) {
673 | newAppDef.entryPoint = lintErrorComponent;
674 | }
675 | discoverCompilationDependencies({
676 | projectCompilation: resolvedRuntime.projectCompilation,
677 | extensionManager,
678 | });
679 | setProjectCompilation(resolvedRuntime.projectCompilation);
680 | setStandaloneApp(newAppDef);
681 | return;
682 | }
683 |
684 | // --- Fetch the main file
685 | const entryPointPromise = new Promise(async (resolve) => {
686 | try {
687 | const resp = await fetchWithoutCache(MAIN_FILE);
688 | if (resp.ok) {
689 | resolve(parseComponentMarkupResponse(resp));
690 | } else {
691 | resolve({
692 | component: errReportMessage(`Failed to load the main component (${MAIN_FILE})`),
693 | file: MAIN_FILE,
694 | hasError: true,
695 | });
696 | }
697 | } catch (e) {
698 | resolve(null);
699 | }
700 | }) as any;
701 |
702 | // --- Fetch the configuration file (we do not check whether the content is semantically valid)
703 | let config: StandaloneJsonConfig = undefined;
704 | try {
705 | const configResponse = await fetchWithoutCache(CONFIG_FILE);
706 | config = await configResponse.json();
707 | } catch (e) {}
708 |
709 | // --- Fetch the themes according to the configuration
710 | let themePromises: Promise<ThemeDefinition>[];
711 | if ((config?.themes ?? []).length === 0 && config?.defaultTheme) {
712 | // --- Special case, we have only a single "defaultTheme" in the configuration
713 | const fetchDefaultTheme = loadThemeFile(`themes/${config?.defaultTheme}.json`);
714 | themePromises = [fetchDefaultTheme];
715 | } else {
716 | // --- In any other case, we fetch all themes defined in the configuration
717 | themePromises = config?.themes?.map((themePath) => {
718 | return loadThemeFile(themePath);
719 | });
720 | }
721 | // --- Fetch component files according to the configuration
722 | const componentPromises = config?.components?.map(async (componentPath) => {
723 | const value = await fetchWithoutCache(componentPath);
724 | if (componentPath.endsWith(`.${componentFileExtension}`)) {
725 | return await parseComponentMarkupResponse(value);
726 | } else {
727 | return await parseCodeBehindResponse(value);
728 | }
729 | });
730 |
731 | // --- Let the promises resolve
732 | const [loadedEntryPoint, loadedComponents, themes] = await Promise.all([
733 | entryPointPromise,
734 | Promise.all(componentPromises || []),
735 | Promise.all(themePromises || []),
736 | ]);
737 |
738 | // --- Collect the elements of the standalone app (and potential errors)
739 | const errorComponents: ComponentDef[] = [];
740 |
741 | // --- Check if the main component has errors
742 | if (loadedEntryPoint.hasError) {
743 | errorComponents.push(loadedEntryPoint!.component as ComponentDef);
744 | }
745 |
746 | let loadedEntryPointCodeBehind = null;
747 | if (loadedEntryPoint.component.props?.codeBehind !== undefined) {
748 | // --- We have a code-behind file for the main component
749 | loadedEntryPointCodeBehind = (await new Promise(async (resolve) => {
750 | try {
751 | const resp = await fetchWithoutCache(
752 | resolvePath(
753 | MAIN_FILE,
754 | loadedEntryPoint.component.props?.codeBehind || MAIN_CODE_BEHIND_FILE,
755 | ),
756 | );
757 | const codeBehind = await parseCodeBehindResponse(resp);
758 | resolve(codeBehind.hasError ? codeBehind : codeBehind.codeBehind);
759 | } catch (e) {
760 | resolve(null);
761 | }
762 | })) as any;
763 | if (loadedEntryPointCodeBehind.hasError) {
764 | errorComponents.push(loadedEntryPointCodeBehind.component as ComponentDef);
765 | }
766 | }
767 |
768 | // --- Check if any of the components have markup errors
769 | loadedComponents.forEach((compWrapper) => {
770 | if (compWrapper.hasError) {
771 | errorComponents.push(compWrapper.component as ComponentDef);
772 | }
773 | });
774 |
775 | // --- Collect the sources and code-behinds
776 | const sources: Record<string, string> = {};
777 | const codeBehinds: any = {};
778 |
779 | // --- The main component source
780 | if (loadedEntryPoint.file) {
781 | sources[loadedEntryPoint.file] = loadedEntryPoint.src;
782 |
783 | resolvedRuntime.projectCompilation.entrypoint.filename = MAIN_FILE;
784 | resolvedRuntime.projectCompilation.entrypoint.definition = loadedEntryPoint.component;
785 | resolvedRuntime.projectCompilation.entrypoint.markupSource = loadedEntryPoint.src;
786 |
787 | if (loadedEntryPointCodeBehind?.src !== undefined) {
788 | resolvedRuntime.projectCompilation.entrypoint.codeBehindSource =
789 | loadedEntryPointCodeBehind.src;
790 | }
791 | }
792 |
793 | // --- Components may have code-behind files
794 | loadedComponents.forEach((compWrapper) => {
795 | if (compWrapper?.file?.endsWith(`.${codeBehindFileExtension}`)) {
796 | codeBehinds[compWrapper.file] = compWrapper.codeBehind;
797 | } else {
798 | if (compWrapper.file) {
799 | sources[compWrapper.file] = compWrapper.src;
800 | // resolvedRuntime.projectCompilation.entrypoint.filename = MAIN_FILE;
801 | // resolvedRuntime.projectCompilation.entrypoint.definition = loadedEntryPoint.component;
802 | // resolvedRuntime.projectCompilation.entrypoint.markupSource = loadedEntryPoint.src;
803 | }
804 | }
805 | });
806 |
807 | // --- Assemble the runtime for the main app file
808 | const entryPointWithCodeBehind = {
809 | ...loadedEntryPoint.component,
810 | vars: {
811 | ...loadedEntryPointCodeBehind?.vars,
812 | ...loadedEntryPoint.component.vars,
813 | },
814 | functions: loadedEntryPointCodeBehind?.functions,
815 | scriptError: loadedEntryPointCodeBehind?.moduleErrors,
816 | };
817 |
818 | const defaultTheme = (entryPointWithCodeBehind as ComponentDef).props?.defaultTheme;
819 | // --- We test whether the default theme is not from a binding
820 | // --- expression and is not a built-in theme already loaded. If so,
821 | // --- we load it from the `themes` folder.
822 | if (defaultTheme && typeof defaultTheme === "string" && !defaultTheme.includes("{")) {
823 | if (
824 | !builtInThemes.find((theme) => theme.id === defaultTheme) &&
825 | !themes.find((theme) => theme.id === defaultTheme)
826 | ) {
827 | themes.push(await loadThemeFile(`themes/${defaultTheme}.json`));
828 | }
829 | }
830 |
831 | // --- Assemble the runtime for the components
832 | const componentsWithCodeBehinds = loadedComponents
833 | .filter((compWrapper) => !compWrapper?.file?.endsWith(".xmlui.xs"))
834 | .map((compWrapper) => {
835 | const componentCodeBehind = codeBehinds[compWrapper.file + ".xs"];
836 | return {
837 | ...compWrapper.component,
838 | component: {
839 | ...(compWrapper.component as any).component,
840 | vars: {
841 | ...(compWrapper.component as any).component.vars,
842 | ...componentCodeBehind?.vars,
843 | },
844 | functions: componentCodeBehind?.functions,
845 | scriptError: componentCodeBehind?.moduleErrors,
846 | },
847 | };
848 | });
849 |
850 | // --- We may have components that are not in the configuration file.
851 | // --- We need to load them and their code-behinds. First, we collect
852 | // --- the components to load.
853 | let componentsToLoad = collectMissingComponents(
854 | entryPointWithCodeBehind,
855 | componentsWithCodeBehinds,
856 | undefined,
857 | extensionManager,
858 | );
859 |
860 | // --- Try to load the components referenced in the markup, collect
861 | // --- those that failed
862 | const componentsFailedToLoad = new Set();
863 | while (componentsToLoad.size > 0) {
864 | const componentPromises = [...componentsToLoad].map(async (componentPath) => {
865 | try {
866 | // --- Promises for the component markup files
867 | const componentPromise = fetchWithoutCache(
868 | `components/${componentPath}.${componentFileExtension}`,
869 | );
870 |
871 | // --- Let the promises resolve
872 | const componentMarkup = await componentPromise;
873 |
874 | // --- Parse the component markup and check for errors
875 | const compWrapper = await parseComponentMarkupResponse(componentMarkup);
876 | if (compWrapper.hasError) {
877 | errorComponents.push(compWrapper.component as ComponentDef);
878 | }
879 |
880 | sources[compWrapper.file] = compWrapper.src;
881 | const compCompilation: ComponentCompilation = {
882 | dependencies: new Set(),
883 | filename: compWrapper.file,
884 | markupSource: compWrapper.src,
885 | definition: compWrapper.component as CompoundComponentDef,
886 | };
887 |
888 | let componentCodeBehind = null;
889 | if (
890 | "codeBehind" in compWrapper.component &&
891 | compWrapper.component?.codeBehind !== undefined
892 | ) {
893 | // --- Promises for the component code-behind files
894 | componentCodeBehind = (await new Promise(async (resolve) => {
895 | try {
896 | const codeBehind = await fetchWithoutCache(
897 | resolvePath(
898 | `components/${componentPath}`,
899 | (compWrapper.component as CompoundComponentDef)?.codeBehind ||
900 | `${componentPath}.${codeBehindFileExtension}`,
901 | ),
902 | );
903 | const codeBehindWrapper = await parseCodeBehindResponse(codeBehind);
904 | if (codeBehindWrapper.hasError) {
905 | errorComponents.push(codeBehindWrapper.component as ComponentDef);
906 | }
907 | resolve(
908 | codeBehindWrapper.hasError
909 | ? (codeBehindWrapper.component as CompoundComponentDef)
910 | : codeBehindWrapper,
911 | );
912 | } catch {
913 | resolve(null);
914 | }
915 | })) as Promise<CompoundComponentDef | ParsedResponse>;
916 |
917 | if (componentCodeBehind && "src" in componentCodeBehind) {
918 | compCompilation.codeBehindSource = componentCodeBehind.src;
919 | }
920 | }
921 |
922 | resolvedRuntime.projectCompilation.components.push(compCompilation);
923 |
924 | const compoundComp = {
925 | ...compWrapper.component,
926 | component: {
927 | ...(compWrapper.component as any).component,
928 | vars: {
929 | ...(compWrapper.component as any).component.vars,
930 | ...componentCodeBehind?.codeBehind?.vars,
931 | },
932 | },
933 | };
934 |
935 | if (componentCodeBehind && "codeBehind" in componentCodeBehind) {
936 | compoundComp.component.functions = componentCodeBehind.codeBehind.functions;
937 | compoundComp.component.scriptError = componentCodeBehind.codeBehind.moduleErrors;
938 | }
939 |
940 | return compoundComp;
941 | } catch (e) {
942 | componentsFailedToLoad.add(componentPath);
943 | return null;
944 | }
945 | });
946 | const componentWrappers = await Promise.all(componentPromises);
947 | componentsWithCodeBehinds.push(...componentWrappers.filter((comp) => !!comp));
948 | componentsToLoad = collectMissingComponents(
949 | entryPointWithCodeBehind,
950 | componentsWithCodeBehinds,
951 | componentsFailedToLoad,
952 | extensionManager,
953 | );
954 | }
955 | // --- Let's check for errors to display
956 |
957 | const newAppDef = {
958 | ...config,
959 | themes,
960 | sources,
961 | components: componentsWithCodeBehinds as any,
962 | entryPoint: entryPointWithCodeBehind,
963 | };
964 |
965 | const lintErrorComponent = processAppLinting(newAppDef, metadataProvider);
966 |
967 | const errorComponent: ComponentDef | null =
968 | errorComponents.length > 0
969 | ? {
970 | type: "VStack",
971 | props: { gap: 0, padding: 0 },
972 | children: errorComponents,
973 | }
974 | : null;
975 | if (errorComponent) {
976 | if (lintErrorComponent) {
977 | errorComponent.children!.push(lintErrorComponent);
978 | }
979 | newAppDef.entryPoint = errorComponent;
980 | } else if (lintErrorComponent) {
981 | newAppDef.entryPoint = lintErrorComponent;
982 | }
983 |
984 | discoverCompilationDependencies({
985 | projectCompilation: resolvedRuntime.projectCompilation,
986 | extensionManager,
987 | });
988 |
989 | setProjectCompilation(resolvedRuntime.projectCompilation);
990 | setStandaloneApp(newAppDef);
991 | })();
992 | }, [runtime, standaloneAppDef]);
993 | return { standaloneApp, projectCompilation };
994 | }
995 |
996 | /**
997 | * Collect the missing components referenced by any part of the app
998 | * @param entryPoint The app's main markup
999 | * @param components The component markups
1000 | * @param componentsFailedToLoad The components that failed to load here
1001 | * @returns The components that are still missing
1002 | */
1003 | function collectMissingComponents(
1004 | entryPoint: ComponentDef | CompoundComponentDef,
1005 | components: any[],
1006 | componentsFailedToLoad = new Set(),
1007 | extensionManager?: StandaloneExtensionManager,
1008 | ) {
1009 | // --- Add the discovered compound components to the registry
1010 | const componentRegistry = new ComponentRegistry(
1011 | { compoundComponents: components },
1012 | extensionManager,
1013 | );
1014 |
1015 | // --- Check the xmlui markup. This check will find all unloaded components
1016 | const result = checkXmlUiMarkup(entryPoint as ComponentDef, components, {
1017 | getComponentProps: (componentName) => {
1018 | return componentRegistry.lookupComponentRenderer(componentName)?.descriptor?.props;
1019 | },
1020 | acceptArbitraryProps: () => {
1021 | return true;
1022 | },
1023 | getComponentValidator: () => {
1024 | return null;
1025 | },
1026 | getComponentEvents: () => {
1027 | return null;
1028 | },
1029 | componentRegistered: (componentName) => {
1030 | return componentRegistry.hasComponent(componentName);
1031 | },
1032 | });
1033 |
1034 | componentRegistry.destroy();
1035 |
1036 | // --- Collect all missing components.
1037 | // Omit the components that failed to load and the ones that are not in #app-ns namespace
1038 | return new Set(
1039 | result
1040 | .filter((r) => r.code === "M001")
1041 | .map((r) => r.args[0].replace("#app-ns.", ""))
1042 | .filter((comp) => !componentsFailedToLoad.has(comp) && !comp.includes(".")),
1043 | );
1044 | }
1045 |
1046 | // --- This React hook logs the app's version number to the browser's console at startup
1047 | function usePrintVersionNumber(standaloneApp: StandaloneAppDescription | null) {
1048 | const logged = useRef(false);
1049 | useEffect(() => {
1050 | if (logged.current) {
1051 | return;
1052 | }
1053 | logged.current = true;
1054 | let log = `XMLUI version: ${process.env.VITE_XMLUI_VERSION || "dev"}`;
1055 | if (standaloneApp?.name) {
1056 | log += `; ${standaloneApp.name} version: ${process.env.VITE_APP_VERSION || "dev"}`;
1057 | }
1058 | console.log(log);
1059 | }, [standaloneApp?.name]);
1060 | }
1061 |
1062 | let contentRoot: Root | null = null;
1063 |
1064 | /**
1065 | * This function injects the StandaloneApp component into a React app. It looks
1066 | * up a component with the "root" id as the host of the standalone app. If such
1067 | * an element does not exist, it creates a `<div id="root">` element.
1068 | * @param runtime The app's runtime representation
1069 | * @param components The related component's runtime representation
1070 | * @returns The content's root element
1071 | */
1072 |
1073 | export function startApp(
1074 | runtime: any,
1075 | extensions: Extension[] | Extension = [],
1076 | extensionManager: StandaloneExtensionManager = new StandaloneExtensionManager(),
1077 | ) {
1078 | extensionManager.registerExtension(extensions);
1079 | let rootElement: HTMLElement | null = document.getElementById("root");
1080 | if (!rootElement) {
1081 | rootElement = document.createElement("div");
1082 | rootElement.setAttribute("id", "root");
1083 | document.body.appendChild(rootElement);
1084 | }
1085 | if (!contentRoot) {
1086 | contentRoot = ReactDOM.createRoot(rootElement);
1087 | }
1088 | contentRoot.render(<StandaloneApp runtime={runtime} extensionManager={extensionManager} />);
1089 | return contentRoot;
1090 | }
1091 |
1092 | export default StandaloneApp;
1093 |
1094 | function processAppLinting(
1095 | appDef: StandaloneAppDescription,
1096 | metadataProvider: MetadataProvider,
1097 | ): null | ComponentDef {
1098 | const lintSeverity = getLintSeverity(appDef.appGlobals?.lintSeverity);
1099 |
1100 | if (lintSeverity !== LintSeverity.Skip) {
1101 | const allComponentLints = lintApp({
1102 | appDef,
1103 | metadataProvider,
1104 | });
1105 |
1106 | if (allComponentLints.length > 0) {
1107 | if (lintSeverity === LintSeverity.Warning) {
1108 | allComponentLints.forEach(printComponentLints);
1109 | } else if (lintSeverity === LintSeverity.Error) {
1110 | return lintErrorsComponent(allComponentLints);
1111 | }
1112 | }
1113 | return null;
1114 | }
1115 | }
1116 |
1117 | function discoverCompilationDependencies({
1118 | projectCompilation: { components, entrypoint },
1119 | extensionManager,
1120 | }: {
1121 | projectCompilation: ProjectCompilation;
1122 | extensionManager?: StandaloneExtensionManager;
1123 | }) {
1124 | const registry = new ComponentRegistry({}, extensionManager);
1125 |
1126 | const entrypointDependencies = discoverDirectComponentDependencies(
1127 | entrypoint.definition,
1128 | registry,
1129 | );
1130 | entrypoint.dependencies = entrypointDependencies;
1131 |
1132 | for (const componentCompilation of components) {
1133 | const compDependencies = discoverDirectComponentDependencies(
1134 | componentCompilation.definition.component,
1135 | registry,
1136 | );
1137 | compDependencies.delete(componentCompilation.definition.name);
1138 | componentCompilation.dependencies = compDependencies;
1139 | }
1140 |
1141 | registry.destroy();
1142 | }
1143 |
1144 | function discoverDirectComponentDependencies(
1145 | entrypoint: ComponentDef,
1146 | registry: ComponentRegistry,
1147 | ): Set<string> {
1148 | return discoverDirectComponentDependenciesHelp(entrypoint, registry, new Set<string>());
1149 | }
1150 |
1151 | function discoverDirectComponentDependenciesHelp(
1152 | component: ComponentDef,
1153 | registry: ComponentRegistry,
1154 | deps: Set<string>,
1155 | ): Set<string> {
1156 | if (!component) {
1157 | return deps;
1158 | }
1159 | const compName = component.type;
1160 | if (!registry.hasComponent(compName)) {
1161 | deps.add(compName);
1162 | }
1163 | if (!component.children) {
1164 | return deps;
1165 | }
1166 |
1167 | for (const child of component.children) {
1168 | discoverDirectComponentDependenciesHelp(child, registry, deps);
1169 | }
1170 |
1171 | return deps;
1172 | }
```