Full Code of vuejs/devtools-v6 for AI

main dd2ab5d42751 cached
404 files
910.8 KB
248.1k tokens
775 symbols
1 requests
Download .txt
Showing preview only (1,008K chars total). Download the full file or copy to clipboard to get everything.
Repository: vuejs/devtools-v6
Branch: main
Commit: dd2ab5d42751
Files: 404
Total size: 910.8 KB

Directory structure:
gitextract_85f6hf0f/

├── .browserslistrc
├── .circleci/
│   └── config.yml
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── commit-convention.md
│   └── workflows/
│       └── create-release.yml
├── .gitignore
├── .vscode/
│   └── settings.json
├── LICENSE
├── README.md
├── babel.config.js
├── cypress/
│   ├── .eslintrc.js
│   ├── .gitignore
│   ├── fixtures/
│   │   └── example.json
│   ├── integration/
│   │   ├── component-data-edit.js
│   │   ├── components-tab.js
│   │   ├── events-tab.js
│   │   ├── vuex-edit.js
│   │   └── vuex-tab.js
│   ├── plugins/
│   │   └── index.js
│   ├── support/
│   │   ├── commands.js
│   │   └── index.js
│   └── utils/
│       └── suite.js
├── cypress.json
├── eslint.config.js
├── extension-zips.js
├── lerna.json
├── package.json
├── packages/
│   ├── api/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── api/
│   │   │   │   ├── api.ts
│   │   │   │   ├── app.ts
│   │   │   │   ├── component.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── hooks.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── util.ts
│   │   │   ├── const.ts
│   │   │   ├── env.ts
│   │   │   ├── index.ts
│   │   │   ├── plugin.ts
│   │   │   ├── proxy.ts
│   │   │   └── time.ts
│   │   └── tsconfig.json
│   ├── app-backend-api/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── api.ts
│   │   │   ├── app-record.ts
│   │   │   ├── backend-context.ts
│   │   │   ├── backend.ts
│   │   │   ├── global-hook.ts
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   └── plugin.ts
│   │   └── tsconfig.json
│   ├── app-backend-core/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── app.ts
│   │   │   ├── backend.ts
│   │   │   ├── component-pick.ts
│   │   │   ├── component.ts
│   │   │   ├── flash.ts
│   │   │   ├── global-hook.ts
│   │   │   ├── highlighter.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   ├── inspector.ts
│   │   │   ├── legacy/
│   │   │   │   └── scan.ts
│   │   │   ├── page-config.ts
│   │   │   ├── perf.ts
│   │   │   ├── plugin.ts
│   │   │   ├── timeline-builtins.ts
│   │   │   ├── timeline-marker.ts
│   │   │   ├── timeline-screenshot.ts
│   │   │   ├── timeline.ts
│   │   │   ├── toast.ts
│   │   │   └── util/
│   │   │       ├── queue.ts
│   │   │       └── subscriptions.ts
│   │   └── tsconfig.json
│   ├── app-backend-vue1/
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── app-backend-vue2/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   ├── data.ts
│   │   │   │   ├── el.ts
│   │   │   │   ├── perf.ts
│   │   │   │   ├── tree.ts
│   │   │   │   ├── update-tracking.ts
│   │   │   │   └── util.ts
│   │   │   ├── events.ts
│   │   │   ├── index.ts
│   │   │   └── plugin.ts
│   │   └── tsconfig.json
│   ├── app-backend-vue3/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   ├── data.ts
│   │   │   │   ├── el.ts
│   │   │   │   ├── filter.ts
│   │   │   │   ├── tree.ts
│   │   │   │   └── util.ts
│   │   │   ├── index.ts
│   │   │   └── util.ts
│   │   └── tsconfig.json
│   ├── app-frontend/
│   │   ├── package.json
│   │   └── src/
│   │       ├── app.ts
│   │       ├── assets/
│   │       │   ├── github-theme/
│   │       │   │   ├── dark.json
│   │       │   │   └── light.json
│   │       │   └── style/
│   │       │       ├── imports.styl
│   │       │       ├── index.postcss
│   │       │       ├── index.styl
│   │       │       ├── transitions.styl
│   │       │       └── variables.styl
│   │       ├── features/
│   │       │   ├── App.vue
│   │       │   ├── apps/
│   │       │   │   ├── AppSelect.vue
│   │       │   │   ├── AppSelectItem.vue
│   │       │   │   ├── AppSelectPane.vue
│   │       │   │   ├── AppSelectPaneItem.vue
│   │       │   │   ├── index.ts
│   │       │   │   └── vue-version-check.ts
│   │       │   ├── bridge/
│   │       │   │   └── index.ts
│   │       │   ├── chrome/
│   │       │   │   ├── index.ts
│   │       │   │   └── pane-visibility.ts
│   │       │   ├── code/
│   │       │   │   └── CodeEditor.vue
│   │       │   ├── components/
│   │       │   │   ├── ComponentTreeNode.vue
│   │       │   │   ├── ComponentsInspector.vue
│   │       │   │   ├── RenderCode.vue
│   │       │   │   ├── SelectedComponentPane.vue
│   │       │   │   └── composable/
│   │       │   │       ├── components.ts
│   │       │   │       ├── highlight.ts
│   │       │   │       ├── index.ts
│   │       │   │       ├── pick.ts
│   │       │   │       └── setup.ts
│   │       │   ├── connection/
│   │       │   │   ├── AppConnecting.vue
│   │       │   │   ├── AppDisconnected.vue
│   │       │   │   └── index.ts
│   │       │   ├── error/
│   │       │   │   ├── ErrorOverlay.vue
│   │       │   │   └── index.ts
│   │       │   ├── header/
│   │       │   │   ├── AppHeader.vue
│   │       │   │   ├── AppHeaderSelect.vue
│   │       │   │   ├── AppHistoryNav.vue
│   │       │   │   ├── header.ts
│   │       │   │   └── tabs.ts
│   │       │   ├── inspector/
│   │       │   │   ├── DataField.vue
│   │       │   │   ├── StateFields.vue
│   │       │   │   ├── StateInspector.vue
│   │       │   │   ├── StateType.vue
│   │       │   │   └── custom/
│   │       │   │       ├── CustomInspector.vue
│   │       │   │       ├── CustomInspectorNode.vue
│   │       │   │       ├── CustomInspectorSelectedNodePane.vue
│   │       │   │       └── composable.ts
│   │       │   ├── layout/
│   │       │   │   ├── EmptyPane.vue
│   │       │   │   ├── SplitPane.vue
│   │       │   │   └── orientation.ts
│   │       │   ├── plugin/
│   │       │   │   ├── PluginDetails.vue
│   │       │   │   ├── PluginHome.vue
│   │       │   │   ├── PluginListItem.vue
│   │       │   │   ├── PluginPermission.vue
│   │       │   │   ├── PluginSettings.vue
│   │       │   │   ├── PluginSettingsItem.vue
│   │       │   │   ├── PluginSourceDescription.vue
│   │       │   │   ├── PluginSourceIcon.vue
│   │       │   │   ├── Plugins.vue
│   │       │   │   └── index.ts
│   │       │   ├── settings/
│   │       │   │   ├── GlobalSettings.vue
│   │       │   │   └── NewTag.vue
│   │       │   ├── timeline/
│   │       │   │   ├── AskScreenshotPermission.vue
│   │       │   │   ├── LayerItem.vue
│   │       │   │   ├── Timeline.vue
│   │       │   │   ├── TimelineEventInspector.vue
│   │       │   │   ├── TimelineEventList.vue
│   │       │   │   ├── TimelineEventListItem.vue
│   │       │   │   ├── TimelineScrollbar.vue
│   │       │   │   ├── TimelineView.vue
│   │       │   │   └── composable/
│   │       │   │       ├── events.ts
│   │       │   │       ├── index.ts
│   │       │   │       ├── layers.ts
│   │       │   │       ├── markers.ts
│   │       │   │       ├── reset.ts
│   │       │   │       ├── screenshot.ts
│   │       │   │       ├── setup.ts
│   │       │   │       ├── store.ts
│   │       │   │       └── time.ts
│   │       │   ├── ui/
│   │       │   │   ├── components/
│   │       │   │   │   ├── VueButton.vue
│   │       │   │   │   ├── VueDisable.vue
│   │       │   │   │   ├── VueDropdown.vue
│   │       │   │   │   ├── VueDropdownButton.vue
│   │       │   │   │   ├── VueFormField.vue
│   │       │   │   │   ├── VueGroup.vue
│   │       │   │   │   ├── VueGroupButton.vue
│   │       │   │   │   ├── VueIcon.vue
│   │       │   │   │   ├── VueInput.vue
│   │       │   │   │   ├── VueLoadingBar.vue
│   │       │   │   │   ├── VueLoadingIndicator.vue
│   │       │   │   │   ├── VueModal.vue
│   │       │   │   │   ├── VueSelect.vue
│   │       │   │   │   ├── VueSelectButton.vue
│   │       │   │   │   ├── VueSwitch.vue
│   │       │   │   │   └── icons.ts
│   │       │   │   ├── composables/
│   │       │   │   │   ├── useDisableScroll.ts
│   │       │   │   │   └── useDisabled.ts
│   │       │   │   └── index.ts
│   │       │   └── welcome/
│   │       │       └── WelcomeSlideshow.vue
│   │       ├── index.ts
│   │       ├── locales/
│   │       │   └── en.js
│   │       ├── mixins/
│   │       │   ├── data-field-edit.js
│   │       │   ├── entry-list.ts
│   │       │   └── keyboard.ts
│   │       ├── plugins/
│   │       │   ├── global-refs.ts
│   │       │   ├── i18n.ts
│   │       │   ├── index.ts
│   │       │   └── responsive.ts
│   │       ├── router.ts
│   │       ├── shims-global.d.ts
│   │       ├── shims-vue.d.ts
│   │       ├── types/
│   │       │   └── vue.d.ts
│   │       └── util/
│   │           ├── color.ts
│   │           ├── defer.ts
│   │           ├── fonts.ts
│   │           ├── format/
│   │           │   ├── index.ts
│   │           │   ├── time.ts
│   │           │   └── value.ts
│   │           ├── keyboard.ts
│   │           ├── queue.ts
│   │           ├── reactivity.ts
│   │           ├── shared-data.ts
│   │           ├── theme.ts
│   │           └── time.ts
│   ├── build-tools/
│   │   ├── package.json
│   │   └── src/
│   │       ├── createConfig.js
│   │       └── index.js
│   ├── docs/
│   │   ├── package.json
│   │   ├── postcss.config.js
│   │   ├── src/
│   │   │   ├── .vitepress/
│   │   │   │   ├── .gitignore
│   │   │   │   ├── config.js
│   │   │   │   └── theme/
│   │   │   │       ├── custom.css
│   │   │   │       └── index.js
│   │   │   ├── assets/
│   │   │   │   └── vue-devtools-architecture.drawio
│   │   │   ├── components/
│   │   │   │   ├── InstallButton.vue
│   │   │   │   └── InstallButtons.vue
│   │   │   ├── devtools-vue3.md
│   │   │   ├── guide/
│   │   │   │   ├── contributing.md
│   │   │   │   ├── custom-vue2-app-scan-selector.md
│   │   │   │   ├── devtools-perf.md
│   │   │   │   ├── faq.md
│   │   │   │   ├── installation.md
│   │   │   │   └── open-in-editor.md
│   │   │   ├── index.md
│   │   │   ├── plugin/
│   │   │   │   ├── api-reference.md
│   │   │   │   └── plugins-guide.md
│   │   │   └── release.md
│   │   └── tailwind.config.cjs
│   ├── shared-utils/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── backend.ts
│   │   │   ├── bridge.ts
│   │   │   ├── consts.ts
│   │   │   ├── edit.ts
│   │   │   ├── env.ts
│   │   │   ├── index.ts
│   │   │   ├── plugin-permissions.ts
│   │   │   ├── plugin-settings.ts
│   │   │   ├── raf.ts
│   │   │   ├── shared-data.ts
│   │   │   ├── shell.ts
│   │   │   ├── storage.ts
│   │   │   ├── throttle.ts
│   │   │   ├── transfer.ts
│   │   │   └── util.ts
│   │   └── tsconfig.json
│   ├── shell-chrome/
│   │   ├── devtools-background.html
│   │   ├── devtools.html
│   │   ├── manifest.json
│   │   ├── package.json
│   │   ├── popups/
│   │   │   ├── disabled.html
│   │   │   ├── disabled.nuxt.html
│   │   │   ├── enabled.html
│   │   │   ├── enabled.nuxt.html
│   │   │   ├── not-found.html
│   │   │   └── popup.css
│   │   ├── src/
│   │   │   ├── backend.js
│   │   │   ├── detector-exec.js
│   │   │   ├── detector.js
│   │   │   ├── devtools-background.js
│   │   │   ├── devtools.js
│   │   │   ├── hook-exec.js
│   │   │   ├── hook.js
│   │   │   ├── proxy.js
│   │   │   └── service-worker.js
│   │   └── webpack.config.js
│   ├── shell-dev-vue2/
│   │   ├── package.json
│   │   ├── public/
│   │   │   ├── target-electron.html
│   │   │   ├── target-iframe.html
│   │   │   └── target.html
│   │   ├── src/
│   │   │   ├── Child.vue
│   │   │   ├── Counter.vue
│   │   │   ├── EventChild.vue
│   │   │   ├── EventChild1.vue
│   │   │   ├── EventChildCond.vue
│   │   │   ├── Events.vue
│   │   │   ├── Functional.vue
│   │   │   ├── Hidden.vue
│   │   │   ├── Init.vue
│   │   │   ├── MyClass.js
│   │   │   ├── NativeTypes.vue
│   │   │   ├── NoProp.vue
│   │   │   ├── Other.vue
│   │   │   ├── RefTester.vue
│   │   │   ├── Target.vue
│   │   │   ├── TransitionExample.vue
│   │   │   ├── VuexObject.vue
│   │   │   ├── dynamic-module.js
│   │   │   ├── iframe-app.js
│   │   │   ├── index.js
│   │   │   ├── router/
│   │   │   │   ├── ChildRoute.vue
│   │   │   │   ├── NamedRoute.vue
│   │   │   │   ├── ParentRoute.vue
│   │   │   │   ├── RouteOne.vue
│   │   │   │   ├── RouteTwo.vue
│   │   │   │   ├── RouteWithAlias.vue
│   │   │   │   ├── RouteWithBeforeEnter.vue
│   │   │   │   ├── RouteWithParams.vue
│   │   │   │   ├── RouteWithProps.vue
│   │   │   │   ├── RouteWithQuery.vue
│   │   │   │   └── Router.vue
│   │   │   ├── router.js
│   │   │   └── store.js
│   │   └── webpack.config.js
│   ├── shell-dev-vue3/
│   │   ├── package.json
│   │   ├── public/
│   │   │   ├── target-iframe.html
│   │   │   └── target.html
│   │   ├── src/
│   │   │   ├── Animation.vue
│   │   │   ├── App.vue
│   │   │   ├── App3.vue
│   │   │   ├── AsyncComponent.vue
│   │   │   ├── AsyncSetup.vue
│   │   │   ├── Child.vue
│   │   │   ├── Condition.vue
│   │   │   ├── DomOrder.vue
│   │   │   ├── EventEmit.vue
│   │   │   ├── EventNesting.vue
│   │   │   ├── Form.vue
│   │   │   ├── FormSection.vue
│   │   │   ├── Functional.vue
│   │   │   ├── Ghost.vue
│   │   │   ├── Heavy.vue
│   │   │   ├── Hello.vue
│   │   │   ├── IframeApp.vue
│   │   │   ├── IndexComponent/
│   │   │   │   └── index.vue
│   │   │   ├── Mixins.vue
│   │   │   ├── NativeTypes.vue
│   │   │   ├── Nested.vue
│   │   │   ├── NestedMore.vue
│   │   │   ├── Other.vue
│   │   │   ├── Provide.vue
│   │   │   ├── SetupDataLike.vue
│   │   │   ├── SetupRender.js
│   │   │   ├── SetupScript.vue
│   │   │   ├── SetupTSScriptProps.vue
│   │   │   ├── SuspenseExample.vue
│   │   │   ├── VModelExample.vue
│   │   │   ├── devtools-plugin/
│   │   │   │   ├── index.js
│   │   │   │   └── simple.js
│   │   │   ├── iframe-app.js
│   │   │   ├── main.js
│   │   │   ├── router/
│   │   │   │   ├── Page1.vue
│   │   │   │   └── Page2.vue
│   │   │   └── store.js
│   │   └── webpack.config.js
│   ├── shell-electron/
│   │   ├── README.md
│   │   ├── app.html
│   │   ├── app.js
│   │   ├── bin.js
│   │   ├── index.js
│   │   ├── package.json
│   │   ├── server.js
│   │   ├── src/
│   │   │   ├── backend.js
│   │   │   ├── devtools.js
│   │   │   └── hook.js
│   │   ├── types/
│   │   │   └── index.d.ts
│   │   ├── webpack.config.js
│   │   └── webpack.node.config.js
│   ├── shell-firefox/
│   │   ├── .gitignore
│   │   ├── copy.sh
│   │   ├── manifest.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── backend.js
│   │   │   ├── background.js
│   │   │   ├── detector.js
│   │   │   ├── devtools-background.js
│   │   │   ├── devtools.js
│   │   │   ├── hook.js
│   │   │   └── proxy.js
│   │   └── webpack.config.js
│   └── shell-host/
│       ├── package.json
│       ├── public/
│       │   └── index.html
│       ├── src/
│       │   ├── DevIframe.vue
│       │   ├── backend.js
│       │   ├── devtools.js
│       │   └── hook.js
│       └── webpack.config.js
├── postcss.config.js
├── release.js
├── sign-firefox.js
├── tailwind.config.js
├── tsconfig.json
└── vue1-test.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .browserslistrc
================================================
Chrome >= 52
Firefox >= 48


================================================
FILE: .circleci/config.yml
================================================
version: 2
jobs:
  build:
    docker:
      # specify the version you desire here
      - image: node:current
      - image: vuejs/ci
    resource_class: medium+

    working_directory: ~/repo

    steps:
      - checkout

      # Download and cache dependencies
      - restore_cache:
          keys:
            - v3-dependencies-{{ checksum "yarn.lock" }}
            # fallback to using the latest cache if no exact match is found
            - v3-dependencies-

      - run: yarn install --pure-lockfile

      - save_cache:
          paths:
            - node_modules
            - ~/.cache/yarn
            - ~/.cache/Cypress
          key: v3-dependencies-{{ checksum "yarn.lock" }}

      # run tests!
      - run: yarn build && yarn test

      - store_artifacts:
          path: cypress/videos
      - store_artifacts:
          path: cypress/screenshots


================================================
FILE: .github/FUNDING.yml
================================================
github: Akryum


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 🐞 Bug report
description: Create a report to help us improve
body:
  - type: markdown
    attributes:
      value: |
        **Before You Start...**

        This form is only for submitting bug reports. If you have a usage question
        or are unsure if this is really a bug, make sure to:

        - Read the [docs](https://devtools.vuejs.org/)
        - Read the [FAQ](https://devtools.vuejs.org/guide/faq.html)
        - Ask on [GitHub Discussions](https://github.com/vuejs/devtools/discussions)
        - Ask on [Discord Chat](https://chat.vuejs.org/)

        Also try to search for your issue - it may have already been answered or even fixed in the development branch.
        However, if you find that an old, closed issue still persists in the latest version,
        you should open a new issue using the form below instead of commenting on the old issue.
  - type: input
    id: version
    attributes:
      label: Vue devtools version
      description: |
        Open your browser Extensions page and find the Vue devtools extension. Then write in this field the version number shown next to the Vue devtools extension name.
    validations:
      required: true
  - type: input
    id: reproduction-link
    attributes:
      label: Link to minimal reproduction
      description: |
        The easiest way to provide a reproduction is by showing the bug in [The SFC Playground](https://sfc.vuejs.org/).
        If it cannot be reproduced in the playground and requires a proper build setup, try [StackBlitz](https://vite.new/vue).
        If neither of these are suitable, you can always provide a GitHub repository.

        The reproduction should be **minimal** - i.e. it should contain only the bare minimum amount of code needed
        to show the bug. See [Bug Reproduction Guidelines](https://github.com/vuejs/core/blob/main/.github/bug-repro-guidelines.md) for more details.

        Please do not just fill in a random link. The issue will be closed if no valid reproduction is provided.
      placeholder: Reproduction Link
    validations:
      required: true
  - type: textarea
    id: steps-to-reproduce
    attributes:
      label: Steps to reproduce & screenshots
      description: |
        What do we need to do after opening your repro in order to make the bug happen in the devtools? Clear and concise reproduction instructions are important for us to be able to triage your issue in a timely manner. Note that you can upload screenshots and use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
      placeholder: Steps to reproduce
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: What is expected?
    validations:
      required: true
  - type: textarea
    id: actually-happening
    attributes:
      label: What is actually happening?
    validations:
      required: true
  - type: textarea
    id: system-info
    attributes:
      label: System Info
      description: Output of `npx envinfo --system --npmPackages vue --binaries --browsers`
      render: shell
      placeholder: System, Binaries, Browsers
    validations:
      required: true
  - type: textarea
    id: additional-comments
    attributes:
      label: Any additional comments?
      description: e.g. some background/context of how you ran into this bug.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: I have a performance issue
    url: https://devtools.vuejs.org/guide/devtools-perf.html
    about: Follow the guide to share performance profiling data with us!
  - name: Questions & Discussions
    url: https://github.com/vuejs/devtools/discussions
    about: Use GitHub discussions for message-board style questions and discussions.
  - name: Discord Chat
    url: https://chat.vuejs.org
    about: Ask questions and discuss with other Vue users in real time.
  - name: GitHub Sponsor
    url: https://github.com/sponsors/Akryum
    about: Love the Vue devtools? Please consider supporting us via GitHub sponsors.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 🚀 New feature proposal
description: Suggest an idea for this project
labels: [':sparkles: feature request']
body:
  - type: markdown
    attributes:
      value: |
        **Before You Start...**

         This form is only for submitting feature requests. If you have a usage question
         or are unsure if this is really a bug, make sure to:

         - Read the [docs](https://devtools.vuejs.org/)
         - Read the [FAQ](https://devtools.vuejs.org/guide/faq.html)
         - Ask on [GitHub Discussions](https://github.com/vuejs/devtools/discussions)
         - Ask on [Discord Chat](https://chat.vuejs.org/)

         Also try to search for your issue - another user may have already requested something similar!

  - type: textarea
    id: problem-description
    attributes:
      label: What problem does this feature solve?
      description: |
        Explain your use case, context, and rationale behind this feature request. More importantly, what is the **end user experience** you are trying to build that led to the need for this feature?
      placeholder: Problem description
    validations:
      required: true


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- Thank you for contributing! -->

### Description

<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->

### Additional context

<!-- e.g. is there anything you'd like reviewers to focus on? -->

---

### What is the purpose of this pull request? <!-- (put an "X" next to an item) -->

- [ ] Bug fix
- [ ] New Feature
- [ ] Documentation update
- [ ] Other

### Before submitting the PR, please make sure you do the following

- [ ] Read the [Contributing Guidelines](https://devtools.vuejs.org/guide/contributing.html).
- [ ] Read the [Pull Request Guidelines](https://devtools.vuejs.org/guide/contributing.html#pull-request-guidelines) and follow the [Commit Convention](https://github.com/vuejs/devtools/blob/main/.github/commit-convention.md).
- [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- [ ] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`).
<!-- @TODO tests - [ ] Ideally, include relevant tests that fail without this PR but pass with it. -->


================================================
FILE: .github/commit-convention.md
================================================
## Git Commit Message Convention

> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).

#### TL;DR:

Messages must be matched by the following regex:

<!-- prettier-ignore -->
```js
/^(revert: )?(feat|fix|docs|dx|refactor|perf|test|workflow|build|ci|chore|types|wip|release|deps)(\(.+\))?: .{1,50}/
```

#### Examples

Appears under "Features" header, `dev` subheader:

```
feat(dev): add 'comments' option
```

Appears under "Bug Fixes" header, `dev` subheader, with a link to issue #28:

```
fix(dev): fix dev error

close #28
```

Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:

```
perf(build): remove 'foo' option

BREAKING CHANGE: The 'foo' option has been removed.
```

The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.

```
revert: feat(compiler): add 'comments' option

This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
```

### Full Message Format

A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:

```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```

The **header** is mandatory and the **scope** of the header is optional.

### Revert

If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.

### Type

If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.

Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.

### Scope

The scope could be anything specifying the place of the commit change. For example `dev`, `build`, `workflow`, `cli` etc...

### Subject

The subject contains a succinct description of the change:

- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize the first letter
- no dot (.) at the end

### Body

Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.

### Footer

The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.

**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.


================================================
FILE: .github/workflows/create-release.yml
================================================
name: Create release

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@master
        with:
          fetch-depth: 0 # Fetch all tags

      - name: Create Release for Tag
        id: release_tag
        uses: Akryum/release-tag@v4.0.7
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}


================================================
FILE: .gitignore
================================================
node_modules
.DS_Store
build
/dist/*
*.zip
*.xpi
tests_output
selenium-debug.log
TODOs.md
.idea
.web-extension-id
yarn-error.log

/packages/*/lib
.amo.env.json
build-node


================================================
FILE: .vscode/settings.json
================================================
{
  "typescript.tsdk": "node_modules/typescript/lib"
}


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014-present Evan You

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# Try the next iteration of Vue Devtools!

We have a brand new version of Devtools being developed at [vuejs/devtools-next](https://github.com/vuejs/devtools-next). It is now in beta, please help us [test it out](https://devtools-next.vuejs.org/getting-started/installation)!

---

# vue-devtools

![screenshot](./media/screenshot-shadow.png)

[Documentation](https://devtools.vuejs.org/) | [Install the extension](https://devtools.vuejs.org/guide/installation.html)

## Monorepo

|Package|Description|
|-------|-----------|
[api](./packages/api) | The public devtools API that can be installed in Vue plugins |
[app-backend-api](./packages/app-backend-api) | Abstract API to link the Public API, the core and Vue handlers |
[app-backend-core](./packages/app-backend-core) | The main logic injected in the page to interact with Vue apps |
[app-backend-vue1](./packages/app-backend-vue1) | Decoupled handlers to support Vue 1 (soon) |
[app-backend-vue2](./packages/app-backend-vue2) | Decoupled handlers to support Vue 2 |
[app-backend-vue3](./packages/app-backend-vue3) | Decoupled handlers to support Vue 3 |
[app-frontend](./packages/app-frontend) | Vue app displayed in the browser devtools pane |
[shell-chrome](./packages/shell-chrome) | Chrome/Firefox extension |
[shell-electron](./packages/shell-electron) | Electron standalone app |
[shell-host](./packages/shell-host) | Development environment |
[shell-dev-vue2](./packages/shell-dev-vue2) | Demo app for development (Vue 2) |
[shell-dev-vue3](./packages/shell-dev-vue3) | Demo app for development (Vue 3) |

## Contributing

See the [Contributing guide](https://devtools.vuejs.org/guide/contributing.html).

## License

[MIT](http://opensource.org/licenses/MIT)

## Sponsors

[💚️ Become a Sponsor](https://github.com/sponsors/Akryum)

<p align="center">
  <a href="https://guillaume-chau.info/sponsors/" target="_blank">
    <img src='https://akryum.netlify.app/sponsors.svg'/>
  </a>
</p>


================================================
FILE: babel.config.js
================================================
module.exports = {
  root: true,
  presets: [
    [
      '@babel/env',
      {
        modules: false,
      },
    ],
  ],
}


================================================
FILE: cypress/.eslintrc.js
================================================
module.exports = {
  plugins: [
    'cypress',
  ],
  env: {
    'mocha': true,
    'cypress/globals': true,
  },
  rules: {
    strict: 'off',
  },
}


================================================
FILE: cypress/.gitignore
================================================
/screenshots
/videos


================================================
FILE: cypress/fixtures/example.json
================================================
{
  "name": "Using fixtures to represent data",
  "email": "hello@cypress.io",
  "body": "Fixtures are a great way to mock data for responses to routes"
}


================================================
FILE: cypress/integration/component-data-edit.js
================================================
import { suite } from '../utils/suite'

suite('component data edit', () => {
  it('should edit data using the decrease button', () => {
    // select Instance
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.component-state-inspector .data-type').should('contain', 'data')
    cy.get('.data-field').eq(7).find('.actions .vue-ui-button').eq(1).click({ force: true })
    cy.get('.component-state-inspector').within(() => {
      cy.get('.key').contains('0').parent().get('.value').contains('0')
    })
    cy.get('.data-field').eq(7).find('.actions .vue-ui-button').eq(1).click({ force: true })
    cy.get('.component-state-inspector').within(() => {
      cy.get('.key').contains('0').parent().contains('-1')
    })

    // expect DOM element to be updated
    cy.get('#target').iframe().then(({ get }) => {
      get('#target div').eq(0).contains('-1')
    })
  })

  it('should edit data using the increase button', () => {
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.component-state-inspector .data-type').should('contain', 'data')
    cy.get('.data-field').eq(7).find('.actions .vue-ui-button').eq(2).click({ force: true })
    cy.get('.component-state-inspector').within(() => {
      cy.get('.key').contains('0').parent().get('.value').contains('0')
    })

    // expect DOM element to be updated
    cy.get('#target').iframe().then(({ get }) => {
      get('#target div').eq(0).contains('0')
    })
  })

  it('should edit data using the edit input', () => {
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.data-field').eq(7).find('.actions .vue-ui-button').eq(0).click({ force: true })

    cy.get('.edit-input').type('12')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.get('.component-state-inspector').within(() => {
      cy.get('.key').contains('0').parent().get('.value').contains('12')
    })

    // expect DOM element to be updated
    cy.get('#target').iframe().then(({ get }) => {
      get('#target div').eq(0).contains('12')
    })
  })

  it('should add elements to array', () => {
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.data-field').eq(6).find('.actions .vue-ui-button').eq(1).click({ force: true })

    cy.get('.edit-input').type('55')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.get('.data-field').eq(6).find('.children .data-field').should('have.length', '3', { timeout: 5000 })
    cy.get('.component-state-inspector').within(() => {
      cy.get('.key').contains('2').parent().get('.value').contains('55')
    })

    // expect DOM element to be updated
    cy.get('#target').iframe().then(({ get }) => {
      get('#target div').eq(4).contains('55')
    })
  })

  it('should remove elements from array', () => {
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.data-field').eq(9).find('.actions .vue-ui-button').eq(3).click({ force: true })

    cy.get('.data-field').eq(6).find('.children .data-field').should('have.length', '2', { timeout: 5000 })
  })

  it('should parse object through edit input', () => {
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.data-field').eq(7).find('.actions .vue-ui-button').eq(0).click({ force: true })

    cy.get('.edit-input').type('{{}"count":42}')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.get('.data-field').eq(7).should('contain', 'Object', { timeout: 5000 })
    // expand object
    cy.get('.data-field').eq(7).click()
    cy.get('.data-field').eq(8).find('.key').should('contain', 'count', { timeout: 5000 })
    cy.get('.data-field').eq(8).find('.value').should('contain', 42, { timeout: 5000 })
  })

  it('should rename object\'s property', () => {
    cy.get('.instance:nth-child(1) .instance:nth-child(2)').eq(0).click()
    cy.get('.data-field').eq(8).find('.actions .vue-ui-button').eq(0).click({ force: true })
    cy.get('.edit-input.key-input').clear().type('name')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.get('.data-field').eq(8).find('.key').should('contain', 'name', { timeout: 5000 })
  })
})


================================================
FILE: cypress/integration/components-tab.js
================================================
import { suite } from '../utils/suite'

const baseInstanceCount = 12

suite('components tab', () => {
  beforeEach(() => cy.reload())

  it('should detect instances inside shadow DOM', () => {
    cy.get('.tree > .instance:last-child').contains('Shadow')
  })

  it('should select instance', () => {
    cy.get('.instance .self').eq(0).click().should('have.class', 'selected')
    cy.get('.tree').should('be.visible')
    cy.get('.action-header .title').contains('Root')
    cy.get('.data-field').contains('$route')
  })

  it('should expand root by default', () => {
    cy.get('.instance').should('have.length', baseInstanceCount)
  })

  it('should detect functional components', () => {
    cy.get('.tree > .instance .instance:nth-child(2)').within(() => {
      cy.get('.arrow').click().then(() => {
        cy.get('.instance:last-child').contains('Functional')
      })
    })
  })

  it('should display 0 key', () => {
    cy.get('.tree > .instance .instance:nth-child(2)').within(() => {
      cy.get('.arrow').click().then(() => {
        cy.get('.instance:nth-child(3) .attr').contains('key=0')
      })
    })
  })

  it('should detect components in transition', () => {
    cy.get('.tree > .instance .instance:nth-child(7)').within(() => {
      cy.get('.arrow').click().then(() => {
        cy.get('.instance').eq(1).within(() => {
          cy.get('.arrow').click().then(() => {
            cy.get('.instance').contains('TestComponent')
          })
        })
      })
    })
  })

  it('should select child instance', () => {
    cy.get('.instance .instance:nth-child(1) .self').eq(0).click()
    cy.get('.action-header .title').contains('Counter')
    cy.get('.data-el.vuex-bindings .data-field').contains('count:0')
    cy.get('.data-el.computed .data-field').contains('test:1')
    cy.get('.data-el.firebase-bindings .data-field').contains('hello:undefined')
  })

  it('should display prop of different types', () => {
    cy.get('.instance .instance:nth-child(2) .self').eq(0).click()
    cy.get('.action-header .title').contains('Target')
    cy.get('.data-el.props .data-field:nth-child(1)').contains('ins:Object')
    cy.get('.data-el.props .data-field:nth-child(2)').contains('msg:"hi"')
    cy.get('.data-el.props .data-field:nth-child(3)').contains('obj:undefined')
    // Regexp
    cy.get('.data-el.data .data-field:nth-child(8)').then((el) => {
      expect(el.text()).to.include('regex:/(a\\w+b)/g')
    })
    // Literals
    cy.get('.data-el.data .data-field:nth-child(5)').contains('NaN')
    cy.get('.data-el.data .data-field:nth-child(2)').contains('Infinity')
    cy.get('.data-el.data .data-field:nth-child(6)').contains('-Infinity')
  })

  it('should expand child instance', () => {
    cy.get('.instance .instance:nth-child(2) .arrow-wrapper').click()
    cy.get('.instance').should('have.length', baseInstanceCount + 10)
  })

  it('should add/remove component from app side', () => {
    cy.get('.instance .instance:nth-child(2) .arrow-wrapper').click()
    cy.get('.instance').should('have.length', baseInstanceCount + 10)
    cy.get('#target').iframe().then(({ get }) => {
      get('.add').click({ force: true })
    })
    cy.get('.instance').should('have.length', baseInstanceCount + 13)
    cy.get('#target').iframe().then(({ get }) => {
      get('.remove').click({ force: true })
    })
    cy.get('.instance').should('have.length', baseInstanceCount + 12)
  })

  it('should filter components', () => {
    cy.get('.left .search input').clear().type('counter')
    cy.get('.instance').should('have.length', 2)
    cy.get('.left .search input').clear().type('target')
    cy.get('.instance').should('have.length', 12)
    cy.get('.left .search input').clear()
  })

  it('should select component', () => {
    cy.get('.select-component').click()
    cy.get('#target').iframe().then(({ get }) => {
      get('.mine').eq(0)
        .trigger('mouseover', { force: true })
        .click({ force: true })
    })
    cy.get('.action-header .title').contains('Mine')
    cy.get('.tree').then((el) => {
      expect(el.text()).to.include('<Mine>')
    })
  })

  it('should display render key', () => {
    cy.get('.instance .instance:nth-child(2) .arrow-wrapper').click()
    cy.get('.instance .self .attr-title').contains('key')
    cy.get('.instance .self .attr-value').contains('1')
  })

  it('should display injected props', () => {
    cy.get('.left .search input').clear().type('Mine')
    cy.get('.instance').eq(1).click()
    cy.get('.right .data-wrapper')
      .should('contain', 'injected')
      .should('contain', 'answer:42')
      .should('contain', 'foo:"bar"')
      .should('contain', 'noop:ƒ noop(a, b, c)')
    cy.get('.left .search input').clear()
  })

  it('should display $refs', () => {
    cy.get('.instance .item-name').contains('RefTester').click()
    cy.get('.right .data-wrapper')
      .should('contain', 'list:Array[4]')
      .should('contain', '<li>')
      .should('contain', 'tester:<p id="testing"')
  })

  it('should display $attrs', () => {
    cy.get('.instance .instance:nth-child(2) .arrow-wrapper').click()
    cy.get('.instance .instance .instance:nth-child(1) .item-name').click()
    cy.get('.right .data-wrapper')
      .should('contain', '$attrs')
      .should('contain', 'attr:"some-attr"')
  })
})


================================================
FILE: cypress/integration/events-tab.js
================================================
import { suite } from '../utils/suite'

suite('events tab', () => {
  it('should display new events counter', () => {
    cy.get('#target').iframe().then(({ get }) => {
      get('.btn-emit-event').click({ force: true })
      get('.btn-emit-event1').click({ force: true })
      get('.btn-emit-event2').click({ force: true })
    })
    cy.get('.events-tab .tag').contains(3)
    cy.get('.events-tab').click()
    cy.get('.events-tab .tag').should('not.be.visible')
  })

  it('should display events', () => {
    cy.get('.history .entry').should('have.length', 3)
  })

  it('should add event', () => {
    cy.get('#target').iframe().then(({ get }) => {
      get('.btn-emit-log-event').click({ force: true })
    })
    cy.get('.history .entry').should('have.length', 4)
  })

  it('should search events', () => {
    cy.get('.left .search input').clear().type('event')
    cy.get('.history .entry[data-active="true"]').should('have.length', 3)
    cy.get('.left .search input').clear().type('<eventchild1>')
    cy.get('.history .entry[data-active="true"]').should('have.length', 1)
    cy.get('.left .search input').clear().type('/^event$/')
    cy.get('.history .entry[data-active="true"]').should('have.length', 1)
    cy.get('.left .search input').clear()
    cy.get('.button.reset').click()
    cy.get('.history .entry[data-active="true"]').should('have.length', 0)
  })
})


================================================
FILE: cypress/integration/vuex-edit.js
================================================
import { suite } from '../utils/suite'

suite('vuex edit', () => {
  it('should edit state using the decrease button', () => {
    cy.get('.vuex-tab').click()
    cy.get('[data-id="load-vuex-state"]').click()

    // using the decrease button
    cy.get('.state .data-field').eq(0)
      .find('.actions .vue-ui-button').eq(1)
      .click({ force: true })

    cy.get('.vuex-state-inspector').within(() => {
      cy.get('.key').contains('count').parent().contains('-1')
    })

    cy.get('.state .data-field').eq(0)
      .find('.actions .vue-ui-button').eq(1)
      .click({ force: true })

    cy.get('.vuex-state-inspector').within(() => {
      cy.get('.key').contains('count').parent().contains('-2')
    })

    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('-2')
    })
  })

  it('should edit state using the increase button', () => {
    // using the increase button
    cy.get('.state .data-field').eq(0).click()
      .find('.actions .vue-ui-button').eq(2)
      .click({ force: true })

    cy.get('.vuex-state-inspector').within(() => {
      cy.get('.key').contains('count').parent().contains('-1')
    })

    cy.get('.state .data-field').eq(0).click()
      .find('.actions .vue-ui-button').eq(2)
      .click({ force: true })

    cy.get('.vuex-state-inspector').within(() => {
      cy.get('.key').contains('count').parent().contains('0')
    })

    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('0')
    })
  })

  it('should edit state using the edit input', () => {
    // using the edit input
    cy.get('.state .data-field').eq(0).click()
      .find('.actions .vue-ui-button').eq(0).click({ force: true })
    cy.get('.edit-input').type('12')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.wait(200)
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('12')
    })

    // change count back to 1
    cy.get('.state .data-field').eq(0).click()
      .find('.actions .vue-ui-button').eq(0).click({ force: true })
    cy.get('.edit-input').type('0')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.wait(200)
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('0')
    })
  })

  it('should edit state nested field', () => {
    // using the decrease button
    cy.get('.data-field > .children > .data-field').eq(4)
      .find('.actions .vue-ui-button').eq(1)
      .click({ force: true })
      .click({ force: true })

    cy.wait(200)
    cy.get('#target').iframe().then(({ get }) => {
      get('#vuex-object pre').contains('-2')
    })

    // using the increase button
    cy.get('.data-field > .children > .data-field').eq(4)
      .find('.actions .vue-ui-button').eq(2)
      .click({ force: true })
      .click({ force: true })

    cy.wait(200)
    cy.get('#target').iframe().then(({ get }) => {
      get('#vuex-object pre').contains('0')
    })

    // using the input
    cy.get('.data-field > .children > .data-field').eq(4)
      .find('.actions .vue-ui-button').eq(0).click({ force: true })
    cy.get('.edit-input').eq(1).type('12')
    cy.get('.edit-overlay > .actions > :nth-child(2) > .content > .vue-ui-icon').click()

    cy.wait(200)
    cy.get('#target').iframe().then(({ get }) => {
      get('#vuex-object pre').contains('12')
    })
  })
})


================================================
FILE: cypress/integration/vuex-tab.js
================================================
import { suite } from '../utils/suite'

suite('vuex tab', () => {
  it('should display mutations history', () => {
    cy.get('#target').iframe().then(({ get }) => {
      get('.increment')
        .click({ force: true })
        .click({ force: true })
      get('.decrement').click({ force: true })
      get('#counter p').contains('1')
    })
    cy.get('.vuex-tab').click()
    cy.get('.history .entry').should('have.length', 6)
    cy.get('[data-id="load-vuex-state"]').click()
    cy.get('.recording-vuex-state').should('not.be.visible')
    cy.get('.loading-vuex-state').should('not.be.visible')
    cy.get('.vuex-state-inspector')
      .should('contain', 'type:"DECREMENT"')
      .should('contain', 'count:1')
    cy.get('.history .entry').eq(5).should('have.class', 'inspected').should('have.class', 'active')
  })

  it('should filter state & getters', () => {
    cy.get('.right .search input').clear().type('cou')
    cy.get('.data-field').should('have.length', 2)
    cy.get('.right .search input').clear().type('no value')
    cy.get('.data-field').should('have.length', 0)
    cy.get('.right .search input').clear()
  })

  it('should filter history', () => {
    cy.get('.left .search input').clear().type('inc')
    cy.get('.history .entry[data-active="true"]').should('have.length', 2)
    cy.get('.history .entry[data-active="true"].inspected').should('have.length', 0)
    cy.get('.history .entry[data-active="true"].active').should('have.length', 0)

    cy.get('.left .search input').clear().type('/dec/i')
    cy.get('.history .entry[data-active="true"]').should('have.length', 1)
    cy.get('.history .entry[data-active="true"].inspected').should('have.length', 0)
    cy.get('.history .entry[data-active="true"].active').should('have.length', 0)

    cy.get('.left .search input').clear().type('/dec)/i')
    cy.get('.history .entry[data-active="true"]').should('have.length', 5)
    cy.get('.history .entry[data-active="true"].inspected').should('have.length', 0)
    cy.get('.history .entry[data-active="true"].active').should('have.length', 1)

    cy.get('.left .search input').clear()
  })

  it('should inspect state', () => {
    cy.get('.history .entry .mutation-type').eq(2).click()
    cy.get('.history .entry').eq(2)
      .should('have.class', 'inspected')
      .should('not.have.class', 'active')
    cy.get('.recording-vuex-state').should('not.be.visible')
    cy.get('.loading-vuex-state').should('not.be.visible')
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('type:"INCREMENT"')
      expect(el.text()).to.include('count:2')
      expect(el.text()).to.include('Error from getter')
    })
    cy.get('.data-field .key').contains('lastCountPayload').click()
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('a:1')
      expect(el.text()).to.include('b:Object')
    })
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('1')
    })
  })

  it('should time-travel', () => {
    cy.get('.history .entry[data-index="4"] .entry-actions .action-time-travel').click({ force: true })
    cy.get('.history .entry[data-index="4"]')
      .should('have.class', 'inspected')
      .should('have.class', 'active')
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('2')
    })

    cy.get('.history .entry[data-index="3"] .mutation-type').click({ force: true })
    cy.get('.history .entry[data-index="3"]')
      .should('have.class', 'inspected')
      .should('not.have.class', 'active')
    cy.get('.history .entry[data-index="4"]')
      .should('not.have.class', 'inspected')
      .should('have.class', 'active')
    cy.get('.recording-vuex-state').should('not.be.visible')
    cy.get('.loading-vuex-state').should('not.be.visible')
    cy.get('.recording-vuex-state').should('not.be.visible')
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('type:"INCREMENT"')
      expect(el.text()).to.include('count:1')
    })
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('2')
    })
    cy.get('.history .entry[data-index="3"] .entry-actions .action-time-travel').click({ force: true })
    cy.get('.history .entry[data-index="3"]')
      .should('have.class', 'inspected')
      .should('have.class', 'active')
    cy.get('.history .entry[data-index="4"]')
      .should('not.have.class', 'inspected')
      .should('not.have.class', 'active')
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('1')
    })

    // Base state
    cy.get('.history .entry[data-index="0"] .mutation-type').click({ force: true })
    cy.get('.history .entry[data-index="0"]')
      .should('have.class', 'inspected')
      .should('not.have.class', 'active')
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('count:0')
    })
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('1')
    })
    cy.get('.history .entry[data-index="2"] .entry-actions .action-time-travel').click({ force: true })
    cy.get('.history .entry[data-index="2"]')
      .should('have.class', 'inspected')
      .should('have.class', 'active')
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('0')
    })
  })

  it('should revert', () => {
    cy.get('.history .entry[data-index="5"] .mutation-type').click({ force: true })
    cy.get('.history .entry[data-index="5"]').find('.action-revert').click({ force: true })
    cy.get('.history .entry[data-active="true"]').should('have.length', 5)
    cy.get('.history .entry[data-index="4"]')
      .should('have.class', 'inspected')
      .should('have.class', 'active')
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('count:2')
    })
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('2')
    })
  })

  it('should commit', () => {
    cy.get('.history .entry[data-index="4"] .action-commit').click({ force: true })
    cy.get('.history .entry[data-active="true"]').should('have.length', 1)
    cy.get('.history .entry[data-index="0"]')
      .should('have.class', 'inspected')
      .should('have.class', 'active')
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('count:2')
    })
    cy.get('#target').iframe().then(({ get }) => {
      get('#counter p').contains('2')
    })
  })

  it('should display getters', () => {
    cy.get('.vuex-state-inspector').within(() => {
      cy.get('.key').contains('count').parent().contains('2')
      cy.get('.key').contains('isPositive').parent().contains('true')
    })
    cy.get('#target').iframe().then(({ get }) => {
      get('.decrement')
        .click({ force: true })
        .click({ force: true })
        .click({ force: true })
      get('#counter p').contains('-1')
    })
    cy.get('.history .entry[data-index="3"]').click({ force: true })
    cy.get('.recording-vuex-state').should('not.be.visible')
    cy.get('.loading-vuex-state').should('not.be.visible')
    cy.get('.vuex-state-inspector').within(() => {
      cy.get('.key').contains('count').parent().contains('-1')
      cy.get('.key').contains('isPositive').parent().contains('false')
    })
  })

  it('should toggle recording', () => {
    cy.get('.toggle-recording')
      .click()
      .contains('Paused')
    cy.get('.toggle-recording .svg-icon').should('not.have.class', 'enabled')
    // should not record
    cy.get('#target').iframe().then(({ get }) => {
      get('.increment').click({ force: true })
    })
    cy.get('.history .entry[data-active="true"]').should('have.length', 4)
  })

  it('should copy vuex state', () => {
    cy.get('.export').click()
    cy.get('.export .message')
      .contains('(Copied to clipboard!)')
      .should('not.be.visible', { timeout: 5000 })
  })

  it('should import vuex state', () => {
    cy.get('.import').click()
    cy.get('.import-state').should('be.visible')
    cy.get('.import-state textarea').clear().type('{{}invalid: json}')
    cy.get('.message.invalid-json').should('be.visible')
    cy.get('.import-state textarea').clear().type('{{}"count":42,"date":"[native Date Fri Dec 22 2017 10:12:04 GMT+0100 (CET)]","nested":{{}"foo":"meow"},"instant":{{}"hey":"hi"}}')
    cy.wait(500)
    cy.get('.message.invalid-json').should('not.be.visible')
    cy.wait(500)
    cy.get('.vuex-state-inspector').then((el) => {
      expect(el.text()).to.include('count:42')
      expect(el.text()).to.include(`date:${new Date('Fri Dec 22 2017 10:12:04 GMT+0100 (CET)')}`)
    })
    cy.get('.import').click()
    cy.get('.import-state').should('not.be.visible')
  })
})


================================================
FILE: cypress/plugins/index.js
================================================
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

module.exports = (_on, _config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config

  // on('before:browser:launch', (browser = {}, args) => {
  //   if (browser.name === 'chrome') {
  //     args.push('--disable-site-isolation-trials')
  //     return args
  //   }
  // })
}


================================================
FILE: cypress/support/commands.js
================================================
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

Cypress.Commands.add('vueCheckInit', () => {
  cy.get('.message .text').should('be.visible', { timeout: 10000 }).then((el) => {
    expect(el.text()).to.include('Ready. Detected Vue')
  })
  cy.get('.instance').eq(0).contains('Root')
})

// Add iframe support until becomes part of the framework
Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe) => {
  const get = selector => cy.wait(500).wrap($iframe.contents().find(selector))

  const el = $iframe[0]
  const iframeDoc = el.contentDocument || el.contentWindow.document
  if (iframeDoc.readyState === 'complete') {
    return Cypress.Promise.resolve({ body: $iframe.contents().find('body'), get })
  }
  return new Cypress.Promise((resolve) => {
    $iframe.on('load', () => {
      resolve({ body: $iframe.contents().find('body'), get })
    })
  })
})


================================================
FILE: cypress/support/index.js
================================================
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')


================================================
FILE: cypress/utils/suite.js
================================================
export function suite(description, tests) {
  describe(description, () => {
    before(() => {
      cy.visit('/')
      cy.vueCheckInit()
    })
    tests()
  })
}


================================================
FILE: cypress.json
================================================
{
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "chromeWebSecurity": false
}


================================================
FILE: eslint.config.js
================================================
const antfu = require('@antfu/eslint-config').default

module.exports = antfu({
  ignores: [
    '**/dist',
  ],
}, {
  rules: {
    'curly': ['error', 'all'],
    'node/prefer-global/process': 'off',
  },
}, {
  files: [
    'packages/shell-dev*/**',
  ],
  rules: {
    'no-console': 'off',
    'unused-imports/no-unused-vars': 'off',
    'vue/require-explicit-emits': 'off',
    'vue/custom-event-name-casing': 'off',
    'vue/no-deprecated-functional-template': 'off',
    'vue/no-deprecated-filter': 'off',
    'vue/no-unused-refs': 'off',
    'vue/require-component-is': 'off',
    'vue/return-in-computed-property': 'off',
  },
}, {
  files: [
    'packages/shell-host/**',
  ],
  rules: {
    'no-console': 'off',
  },
}, {
  files: [
    'package.json',
    'packages/*/package.json',
    'packages/*/manifest.json',
  ],
  rules: {
    'style/eol-last': 'off',
  },
})


================================================
FILE: extension-zips.js
================================================
// require modules
const fs = require('node:fs')
const path = require('node:path')
const process = require('node:process')
const archiver = require('archiver')

const IS_CI = !!(process.env.CIRCLECI || process.env.GITHUB_ACTIONS)
const ProgressBar = !IS_CI ? require('progress') : {}
const readDirGlob = !IS_CI ? require('readdir-glob') : {}

const INCLUDE_GLOBS = [
  'build/**',
  'icons/**',
  'popups/**',
  'devtools.html',
  'devtools-background.html',
  'manifest.json',
  'package.json',
]
// SKIP_GLOBS makes glob searches more efficient
const SKIP_DIR_GLOBS = ['node_modules', 'src']

function bytesToSize(bytes) {
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
  if (bytes === 0) {
    return '0 Byte'
  }
  const i = Number.parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
  return `${Math.round(bytes / 1024 ** i, 2)} ${sizes[i]}`
}

(async () => {
  await writeZip('devtools-chrome.zip', 'shell-chrome')
  await writeZip('devtools-firefox.zip', 'shell-firefox')

  async function writeZip(fileName, packageDir) {
    // create a file to stream archive data to.
    const output = fs.createWriteStream(path.join(__dirname, 'dist', fileName))
    const archive = archiver('zip', {
      zlib: { level: 9 }, // Sets the compression level.
    })

    if (!IS_CI) {
      const status = {
        total: 0,
        cFile: '...',
        cSize: '0 Bytes',
        tBytes: 0,
        tSize: '0 Bytes',
      }

      async function parseFileStats() {
        return new Promise((resolve, reject) => {
          const globber = readDirGlob(path.join('packages', packageDir), { pattern: INCLUDE_GLOBS, skip: SKIP_DIR_GLOBS, mark: true, stat: true })
          globber.on('match', (match) => {
            if (!match.stat.isDirectory()) {
              status.total++
            }
          })
          globber.on('error', (err) => {
            reject(err)
          })
          globber.on('end', () => {
            resolve()
          })
        })
      }
      await parseFileStats().catch((err) => {
        console.error(err)
        process.exit(1)
      })

      const bar = new ProgressBar(`${fileName} @ :tSize [:bar] :current/:total :percent +:cFile@:cSize`, {
        width: 18,
        incomplete: ' ',
        total: status.total,
      })
      bar.tick(0, status)

      archive.on('entry', (entry) => {
        if (!entry.stats.isDirectory()) {
          const n = entry.name
          status.written++
          status.cFile = n.length > 14
            ? `...${n.slice(n.length - 11)}`
            : n
          status.cSize = bytesToSize(entry.stats.size)
          status.tBytes += entry.stats.size
          status.tSize = bytesToSize(status.tBytes)
          bar.tick(1, status)
        }
      })
    }

    const end = new Promise((resolve) => {
      // listen for all archive data to be written
      // 'close' event is fired only when a file descriptor is involved
      output.on('close', () => {
        if (archive.pointer() < 1000) {
          console.warn(`Zip file (${fileName}) is only ${archive.pointer()} bytes`)
        }
        resolve()
      })
    })

    // This event is fired when the data source is drained no matter what was the data source.
    // It is not part of this library but rather from the NodeJS Stream API.
    // @see: https://nodejs.org/api/stream.html#stream_event_end
    output.on('end', () => {
      'nothing'
    })

    // good practice to catch warnings (ie stat failures and other non-blocking errors)
    archive.on('warning', (err) => {
      if (err.code !== 'ENOENT') {
        // throw error
        console.error(err)
        process.exit(1)
      }
    })

    // good practice to catch this error explicitly
    archive.on('error', (err) => {
      console.error(err)
      process.exit(1)
    })

    // pipe archive data to the file
    archive.pipe(output)

    INCLUDE_GLOBS.forEach((glob) => {
      // append files from a glob pattern
      archive.glob(glob, { cwd: path.join('packages', packageDir), skip: SKIP_DIR_GLOBS })
    })

    // finalize the archive (ie we are done appending files but streams have to finish yet)
    // 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
    archive.finalize()

    await end
  }
})()


================================================
FILE: lerna.json
================================================
{
  "npmClient": "yarn",
  "useWorkspaces": true,
  "version": "6.0.0-beta.2",
  "packages": [
    "packages/*"
  ],
  "ignoreChanges": [
    "**/*.md"
  ]
}


================================================
FILE: package.json
================================================
{
  "name": "vue-devtools",
  "version": "6.6.4",
  "private": true,
  "description": "devtools for Vue.js!",
  "workspaces": [
    "packages/*"
  ],
  "author": "Evan You",
  "license": "MIT",
  "homepage": "https://github.com/vuejs/vue-devtools#readme",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/vuejs/vue-devtools.git"
  },
  "bugs": {
    "url": "https://github.com/vuejs/vue-devtools/issues"
  },
  "engines": {
    "node": ">=8.10"
  },
  "scripts": {
    "dev:vue2": "concurrently \"cd packages/shell-dev-vue2 && yarn dev\" \"cd packages/shell-host && yarn dev\"",
    "dev:vue3": "concurrently \"cd packages/shell-dev-vue3 && yarn dev\" \"cd packages/shell-host && yarn dev\"",
    "dev:chrome": "cd packages/shell-chrome && webpack --watch",
    "dev:chrome:prod": "cd packages/shell-chrome && cross-env NODE_ENV=production webpack --watch",
    "dev:firefox": "cd packages/shell-firefox && webpack --watch",
    "dev:electron": "cd packages/shell-electron && npm run dev",
    "build": "lerna run build",
    "build:watch": "lerna run build --scope @vue-devtools/app-backend* --scope @vue-devtools/shared-* --scope @vue/devtools-api && lerna run build:watch --stream --no-sort --concurrency 99",
    "lint": "eslint .",
    "run:firefox": "web-ext run -s packages/shell-firefox -a dist -i src -u http://localhost:8090/target.html",
    "zip": "node ./extension-zips.js",
    "sign:firefox": "node ./sign-firefox.js",
    "release": "npm run test && node release.js && npm run build && npm run zip && npm run pub",
    "release:beta": "cross-env RELEASE_CHANNEL=beta npm run release && npm run sign:firefox",
    "pub": "npm run pub:electron && npm run pub:api",
    "pub:electron": "cd packages/shell-electron && npm publish",
    "pub:api": "cd packages/api && npm publish",
    "test": "npm run lint && npm run test:types:front",
    "test:types:front": "tsc --noEmit",
    "test:e2e": "cross-env PORT=4040 start-server-and-test dev:shell http://localhost:4040 test:e2e:run",
    "test:e2e:run": "cypress run --config baseUrl=http://localhost:4040",
    "test:open": "cypress open --config baseUrl=http://localhost:8100",
    "docs:dev": "cd packages/docs && vitepress dev src",
    "docs:build": "cd packages/docs && vitepress build src",
    "docs:serve": "cd packages/docs && vitepress serve src"
  },
  "devDependencies": {
    "@antfu/eslint-config": "^2.19.1",
    "@tailwindcss/postcss7-compat": "^2.0.4",
    "@types/chrome": "^0.0.139",
    "@types/speakingurl": "^13.0.3",
    "archiver": "^5.3.0",
    "autoprefixer": "^9.1.5",
    "concurrently": "^5.1.0",
    "cross-env": "^5.2.0",
    "cypress": "^3.1.0",
    "eslint": "^9.3.0",
    "execa": "^4.0.3",
    "inquirer": "^6.2.0",
    "lerna": "^4.0.0",
    "postcss-nested": "^4.2.1",
    "rimraf": "^3.0.2",
    "semver": "^5.5.1",
    "start-server-and-test": "^1.7.1",
    "svg-inline-loader": "^0.8.2",
    "tailwindcss": "npm:@tailwindcss/postcss7-compat",
    "vue-loader": "^17.2.2",
    "webpack-dev-server": "^4.15.1"
  },
  "resolutions": {
    "cypress": "=3.4.1",
    "webpack-dev-server": "^4.15.1"
  }
}

================================================
FILE: packages/api/package.json
================================================
{
  "name": "@vue/devtools-api",
  "version": "6.6.4",
  "description": "Interact with the Vue devtools from the page",
  "author": {
    "name": "Guillaume Chau"
  },
  "license": "MIT",
  "repository": {
    "url": "https://github.com/vuejs/vue-devtools.git",
    "type": "git",
    "directory": "packages/api"
  },
  "sideEffects": false,
  "main": "lib/cjs/index.js",
  "browser": "lib/esm/index.js",
  "module": "lib/esm/index.js",
  "types": "lib/esm/index.d.ts",
  "files": [
    "lib/cjs",
    "lib/esm"
  ],
  "publishConfig": {
    "access": "public"
  },
  "scripts": {
    "build": "rimraf lib && yarn build:esm && yarn build:cjs",
    "build:esm": "tsc --module es2015 --outDir lib/esm -d",
    "build:cjs": "tsc --module commonjs --outDir lib/cjs",
    "build:watch": "yarn tsc --module es2015 --outDir lib/esm -d -w --sourceMap"
  },
  "devDependencies": {
    "@types/node": "^20.11.16",
    "@types/webpack-env": "^1.15.1",
    "typescript": "^5.3.3"
  }
}

================================================
FILE: packages/api/src/api/api.ts
================================================
import type { ComponentBounds, Hookable } from './hooks.js'
import type { Context } from './context.js'
import type { ComponentInstance, ComponentState, StateBase } from './component.js'
import type { App } from './app.js'
import type { ID } from './util.js'

export interface DevtoolsPluginApi<TSettings> {
  on: Hookable<Context>
  notifyComponentUpdate: (instance?: ComponentInstance) => void
  addTimelineLayer: (options: TimelineLayerOptions) => void
  addTimelineEvent: (options: TimelineEventOptions) => void
  addInspector: (options: CustomInspectorOptions) => void
  sendInspectorTree: (inspectorId: string) => void
  sendInspectorState: (inspectorId: string) => void
  selectInspectorNode: (inspectorId: string, nodeId: string) => void
  getComponentBounds: (instance: ComponentInstance) => Promise<ComponentBounds>
  getComponentName: (instance: ComponentInstance) => Promise<string>
  getComponentInstances: (app: App) => Promise<ComponentInstance[]>
  highlightElement: (instance: ComponentInstance) => void
  unhighlightElement: () => void
  getSettings: (pluginId?: string) => TSettings
  now: () => number
  /**
   * @private
   */
  setSettings: (values: TSettings) => void
}

export interface AppRecord {
  id: string
  name: string
  instanceMap: Map<string, ComponentInstance>
  rootInstance: ComponentInstance
}

export interface TimelineLayerOptions<TData = any, TMeta = any> {
  id: string
  label: string
  color: number
  skipScreenshots?: boolean
  groupsOnly?: boolean
  ignoreNoDurationGroups?: boolean
  screenshotOverlayRender?: (event: TimelineEvent<TData, TMeta> & ScreenshotOverlayEvent, ctx: ScreenshotOverlayRenderContext) => ScreenshotOverlayRenderResult | Promise<ScreenshotOverlayRenderResult>
}

export interface ScreenshotOverlayEvent {
  layerId: string
  renderMeta: any
}

export interface ScreenshotOverlayRenderContext<TData = any, TMeta = any> {
  screenshot: ScreenshotData
  events: (TimelineEvent<TData, TMeta> & ScreenshotOverlayEvent)[]
  index: number
}

export type ScreenshotOverlayRenderResult = HTMLElement | string | false

export interface ScreenshotData {
  time: number
}

export interface TimelineEventOptions {
  layerId: string
  event: TimelineEvent
  all?: boolean
}

export interface TimelineEvent<TData = any, TMeta = any> {
  time: number
  data: TData
  logType?: 'default' | 'warning' | 'error'
  meta?: TMeta
  groupId?: ID
  title?: string
  subtitle?: string
}

export interface TimelineMarkerOptions {
  id: string
  time: number
  color: number
  label: string
  all?: boolean
}

export interface CustomInspectorOptions {
  id: string
  label: string
  icon?: string
  treeFilterPlaceholder?: string
  stateFilterPlaceholder?: string
  noSelectionText?: string
  actions?: {
    icon: string
    tooltip?: string
    action: () => void | Promise<void>
  }[]
  nodeActions?: {
    icon: string
    tooltip?: string
    action: (nodeId: string) => void | Promise<void>
  }[]
}

export interface CustomInspectorNode {
  id: string
  label: string
  children?: CustomInspectorNode[]
  tags?: InspectorNodeTag[]
}

export interface InspectorNodeTag {
  label: string
  textColor: number
  backgroundColor: number
  tooltip?: string
}

export interface CustomInspectorState {
  [key: string]: (StateBase | Omit<ComponentState, 'type'>)[]
}


================================================
FILE: packages/api/src/api/app.ts
================================================
export type App = any // @TODO


================================================
FILE: packages/api/src/api/component.ts
================================================
import type { InspectorNodeTag } from './api.js'
import type { ID } from './util.js'

export type ComponentInstance = any // @TODO

export interface ComponentTreeNode {
  uid: ID
  id: string
  name: string
  renderKey: string | number
  inactive: boolean
  isFragment: boolean
  hasChildren: boolean
  children: ComponentTreeNode[]
  domOrder?: number[]
  consoleId?: string
  isRouterView?: boolean
  macthedRouteSegment?: string
  tags: InspectorNodeTag[]
  autoOpen: boolean
  meta?: any
}

export interface InspectedComponentData {
  id: string
  name: string
  file: string
  state: ComponentState[]
  functional?: boolean
}

export interface StateBase {
  key: string
  value: any
  editable?: boolean
  objectType?: 'ref' | 'reactive' | 'computed' | 'other'
  raw?: string
}

export interface ComponentStateBase extends StateBase {
  type: string
}

export interface ComponentPropState extends ComponentStateBase {
  meta?: {
    type: string
    required: boolean
    /** Vue 1 only */
    mode?: 'default' | 'sync' | 'once'
  }
}

export type ComponentBuiltinCustomStateTypes = 'function' | 'map' | 'set' | 'reference' | 'component' | 'component-definition' | 'router' | 'store'

export interface ComponentCustomState extends ComponentStateBase {
  value: CustomState
}

export interface CustomState {
  _custom: {
    type: ComponentBuiltinCustomStateTypes | string
    objectType?: string
    display?: string
    tooltip?: string
    value?: any
    abstract?: boolean
    file?: string
    uid?: number
    readOnly?: boolean
    /** Configure immediate child fields */
    fields?: {
      abstract?: boolean
    }
    id?: any
    actions?: {
      icon: string
      tooltip?: string
      action: () => void | Promise<void>
    }[]
    /** internal */
    _reviveId?: number
  }
}

export type ComponentState = ComponentStateBase | ComponentPropState | ComponentCustomState

export interface ComponentDevtoolsOptions {
  hide?: boolean
}


================================================
FILE: packages/api/src/api/context.ts
================================================
import type { AppRecord } from './api.js'

export interface Context {
  currentTab: string
  currentAppRecord: AppRecord
}


================================================
FILE: packages/api/src/api/hooks.ts
================================================
import type { ComponentDevtoolsOptions, ComponentInstance, ComponentTreeNode, InspectedComponentData } from './component.js'
import type { App } from './app.js'
import type { CustomInspectorNode, CustomInspectorState, TimelineEvent } from './api.js'

// eslint-disable-next-line no-restricted-syntax
export const enum Hooks {
  TRANSFORM_CALL = 'transformCall',
  GET_APP_RECORD_NAME = 'getAppRecordName',
  GET_APP_ROOT_INSTANCE = 'getAppRootInstance',
  REGISTER_APPLICATION = 'registerApplication',
  WALK_COMPONENT_TREE = 'walkComponentTree',
  VISIT_COMPONENT_TREE = 'visitComponentTree',
  WALK_COMPONENT_PARENTS = 'walkComponentParents',
  INSPECT_COMPONENT = 'inspectComponent',
  GET_COMPONENT_BOUNDS = 'getComponentBounds',
  GET_COMPONENT_NAME = 'getComponentName',
  GET_COMPONENT_INSTANCES = 'getComponentInstances',
  GET_ELEMENT_COMPONENT = 'getElementComponent',
  GET_COMPONENT_ROOT_ELEMENTS = 'getComponentRootElements',
  EDIT_COMPONENT_STATE = 'editComponentState',
  GET_COMPONENT_DEVTOOLS_OPTIONS = 'getAppDevtoolsOptions',
  GET_COMPONENT_RENDER_CODE = 'getComponentRenderCode',
  INSPECT_TIMELINE_EVENT = 'inspectTimelineEvent',
  TIMELINE_CLEARED = 'timelineCleared',
  GET_INSPECTOR_TREE = 'getInspectorTree',
  GET_INSPECTOR_STATE = 'getInspectorState',
  EDIT_INSPECTOR_STATE = 'editInspectorState',
  SET_PLUGIN_SETTINGS = 'setPluginSettings',
}

export interface ComponentBounds {
  left: number
  top: number
  width: number
  height: number
}

export interface HookPayloads {
  [Hooks.TRANSFORM_CALL]: {
    callName: string
    inArgs: any[]
    outArgs: any[]
  }
  [Hooks.GET_APP_RECORD_NAME]: {
    app: App
    name: string
  }
  [Hooks.GET_APP_ROOT_INSTANCE]: {
    app: App
    root: ComponentInstance
  }
  [Hooks.REGISTER_APPLICATION]: {
    app: App
  }
  [Hooks.WALK_COMPONENT_TREE]: {
    componentInstance: ComponentInstance
    componentTreeData: ComponentTreeNode[]
    maxDepth: number
    filter: string
    recursively: boolean
  }
  [Hooks.VISIT_COMPONENT_TREE]: {
    app: App
    componentInstance: ComponentInstance
    treeNode: ComponentTreeNode
    filter: string
  }
  [Hooks.WALK_COMPONENT_PARENTS]: {
    componentInstance: ComponentInstance
    parentInstances: ComponentInstance[]
  }
  [Hooks.INSPECT_COMPONENT]: {
    app: App
    componentInstance: ComponentInstance
    instanceData: InspectedComponentData
  }
  [Hooks.GET_COMPONENT_BOUNDS]: {
    componentInstance: ComponentInstance
    bounds: ComponentBounds
  }
  [Hooks.GET_COMPONENT_NAME]: {
    componentInstance: ComponentInstance
    name: string
  }
  [Hooks.GET_COMPONENT_INSTANCES]: {
    app: App
    componentInstances: ComponentInstance[]
  }
  [Hooks.GET_ELEMENT_COMPONENT]: {
    element: HTMLElement | any
    componentInstance: ComponentInstance
  }
  [Hooks.GET_COMPONENT_ROOT_ELEMENTS]: {
    componentInstance: ComponentInstance
    rootElements: (HTMLElement | any)[]
  }
  [Hooks.EDIT_COMPONENT_STATE]: {
    app: App
    componentInstance: ComponentInstance
    path: string[]
    type: string
    state: EditStatePayload
    set: (object: any, path?: string | (string[]), value?: any, cb?: (object: any, field: string, value: any) => void) => void
  }
  [Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS]: {
    componentInstance: ComponentInstance
    options: ComponentDevtoolsOptions
  }
  [Hooks.GET_COMPONENT_RENDER_CODE]: {
    componentInstance: ComponentInstance
    code: string
  }
  [Hooks.INSPECT_TIMELINE_EVENT]: {
    app: App
    layerId: string
    event: TimelineEvent
    all?: boolean
    data: any
  }
  [Hooks.TIMELINE_CLEARED]: Record<string, never>
  [Hooks.GET_INSPECTOR_TREE]: {
    app: App
    inspectorId: string
    filter: string
    rootNodes: CustomInspectorNode[]
  }
  [Hooks.GET_INSPECTOR_STATE]: {
    app: App
    inspectorId: string
    nodeId: string
    state: CustomInspectorState
  }
  [Hooks.EDIT_INSPECTOR_STATE]: {
    app: App
    inspectorId: string
    nodeId: string
    path: string[]
    type: string
    state: EditStatePayload
    set: (object: any, path?: string | (string[]), value?: any, cb?: (object: any, field: string, value: any) => void) => void
  }
  [Hooks.SET_PLUGIN_SETTINGS]: {
    app: App
    pluginId: string
    key: string
    newValue: any
    oldValue: any
    settings: any
  }
}

export type EditStatePayload = {
  value: any
  newKey?: string | null
  remove?: undefined | false
} | {
  value?: undefined
  newKey?: undefined
  remove: true
}

export type HookHandler<TPayload, TContext> = (payload: TPayload, ctx: TContext) => void | Promise<void>

export interface Hookable<TContext> {
  transformCall: (handler: HookHandler<HookPayloads[Hooks.TRANSFORM_CALL], TContext>) => any
  getAppRecordName: (handler: HookHandler<HookPayloads[Hooks.GET_APP_RECORD_NAME], TContext>) => any
  getAppRootInstance: (handler: HookHandler<HookPayloads[Hooks.GET_APP_ROOT_INSTANCE], TContext>) => any
  registerApplication: (handler: HookHandler<HookPayloads[Hooks.REGISTER_APPLICATION], TContext>) => any
  walkComponentTree: (handler: HookHandler<HookPayloads[Hooks.WALK_COMPONENT_TREE], TContext>) => any
  visitComponentTree: (handler: HookHandler<HookPayloads[Hooks.VISIT_COMPONENT_TREE], TContext>) => any
  walkComponentParents: (handler: HookHandler<HookPayloads[Hooks.WALK_COMPONENT_PARENTS], TContext>) => any
  inspectComponent: (handler: HookHandler<HookPayloads[Hooks.INSPECT_COMPONENT], TContext>) => any
  getComponentBounds: (handler: HookHandler<HookPayloads[Hooks.GET_COMPONENT_BOUNDS], TContext>) => any
  getComponentName: (handler: HookHandler<HookPayloads[Hooks.GET_COMPONENT_NAME], TContext>) => any
  getComponentInstances: (handler: HookHandler<HookPayloads[Hooks.GET_COMPONENT_INSTANCES], TContext>) => any
  getElementComponent: (handler: HookHandler<HookPayloads[Hooks.GET_ELEMENT_COMPONENT], TContext>) => any
  getComponentRootElements: (handler: HookHandler<HookPayloads[Hooks.GET_COMPONENT_ROOT_ELEMENTS], TContext>) => any
  editComponentState: (handler: HookHandler<HookPayloads[Hooks.EDIT_COMPONENT_STATE], TContext>) => any
  getComponentDevtoolsOptions: (handler: HookHandler<HookPayloads[Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS], TContext>) => any
  getComponentRenderCode: (handler: HookHandler<HookPayloads[Hooks.GET_COMPONENT_RENDER_CODE], TContext>) => any
  inspectTimelineEvent: (handler: HookHandler<HookPayloads[Hooks.INSPECT_TIMELINE_EVENT], TContext>) => any
  timelineCleared: (handler: HookHandler<HookPayloads[Hooks.TIMELINE_CLEARED], TContext>) => any
  getInspectorTree: (handler: HookHandler<HookPayloads[Hooks.GET_INSPECTOR_TREE], TContext>) => any
  getInspectorState: (handler: HookHandler<HookPayloads[Hooks.GET_INSPECTOR_STATE], TContext>) => any
  editInspectorState: (handler: HookHandler<HookPayloads[Hooks.EDIT_INSPECTOR_STATE], TContext>) => any
  setPluginSettings: (handler: HookHandler<HookPayloads[Hooks.SET_PLUGIN_SETTINGS], TContext>) => any
}


================================================
FILE: packages/api/src/api/index.ts
================================================
export * from './api.js'
export * from './app.js'
export * from './component.js'
export * from './context.js'
export * from './hooks.js'
export * from './util.js'


================================================
FILE: packages/api/src/api/util.ts
================================================
export type ID = number | string

export interface WithId {
  id: ID
}


================================================
FILE: packages/api/src/const.ts
================================================
export const HOOK_SETUP = 'devtools-plugin:setup'
export const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set'


================================================
FILE: packages/api/src/env.ts
================================================
import type { ApiProxy } from './proxy.js'
import type { PluginDescriptor, SetupFunction } from './index.js'

export interface PluginQueueItem {
  pluginDescriptor: PluginDescriptor
  setupFn: SetupFunction
  proxy?: ApiProxy
}

interface GlobalTarget {
  __VUE_DEVTOOLS_PLUGINS__?: PluginQueueItem[]
  __VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__?: boolean
}

export function getDevtoolsGlobalHook(): any {
  return (getTarget() as any).__VUE_DEVTOOLS_GLOBAL_HOOK__
}

export function getTarget(): GlobalTarget {
  // @ts-expect-error navigator and windows are not available in all environments
  return (typeof navigator !== 'undefined' && typeof window !== 'undefined')
    ? window
    : typeof globalThis !== 'undefined'
      ? globalThis
      : {}
}

export const isProxyAvailable = typeof Proxy === 'function'


================================================
FILE: packages/api/src/index.ts
================================================
import { getDevtoolsGlobalHook, getTarget, isProxyAvailable } from './env.js'
import { HOOK_SETUP } from './const.js'
import type { DevtoolsPluginApi } from './api/index.js'
import { ApiProxy } from './proxy.js'
import type { ExtractSettingsTypes, PluginDescriptor, PluginSettingsItem } from './plugin.js'

export * from './api/index.js'
export * from './plugin.js'
export * from './time.js'
export { PluginQueueItem } from './env.js'

// https://github.com/microsoft/TypeScript/issues/30680#issuecomment-752725353
type Cast<A, B> = A extends B ? A : B
type Narrowable =
  | string
  | number
  | bigint
  | boolean
type Narrow<A> = Cast<A, | []
  | (A extends Narrowable ? A : never)
  | ({ [K in keyof A]: Narrow<A[K]> })>

// Prevent properties not in PluginDescriptor
// We need this because of the `extends` in the generic TDescriptor
type Exact<C, T> = {
  [K in keyof C]: K extends keyof T ? T[K] : never
}

export type SetupFunction<TSettings = any> = (api: DevtoolsPluginApi<TSettings>) => void

export function setupDevtoolsPlugin<
  TDescriptor extends Exact<TDescriptor, PluginDescriptor>,
  TSettings = ExtractSettingsTypes<TDescriptor extends { settings: infer S } ? S extends Record<string, PluginSettingsItem> ? S : Record<string, PluginSettingsItem> : Record<string, PluginSettingsItem>>,
>(pluginDescriptor: Narrow<TDescriptor>, setupFn: SetupFunction<TSettings>) {
  const descriptor = pluginDescriptor as unknown as PluginDescriptor
  const target = getTarget()
  const hook = getDevtoolsGlobalHook()
  const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy
  if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
    hook.emit(HOOK_SETUP, pluginDescriptor, setupFn)
  }
  else {
    const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null

    const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || []
    list.push({
      pluginDescriptor: descriptor,
      setupFn,
      proxy,
    })

    if (proxy) {
      setupFn(proxy.proxiedTarget as DevtoolsPluginApi<TSettings>)
    }
  }
}


================================================
FILE: packages/api/src/plugin.ts
================================================
import type { App } from './api/index.js'

export interface PluginDescriptor {
  id: string
  label: string
  app: App
  packageName?: string
  homepage?: string
  componentStateTypes?: string[]
  logo?: string
  disableAppScope?: boolean
  disablePluginScope?: boolean
  /**
   * Run the plugin setup and expose the api even if the devtools is not opened yet.
   * Useful to record timeline events early.
   */
  enableEarlyProxy?: boolean
  settings?: Record<string, PluginSettingsItem>
}

export type PluginSettingsItem = {
  label: string
  description?: string
} & ({
  type: 'boolean'
  defaultValue: boolean
} | {
  type: 'choice'
  defaultValue: string | number
  options: { value: string | number, label: string }[]
  component?: 'select' | 'button-group'
} | {
  type: 'text'
  defaultValue: string
})

type InferSettingsType<
  T extends PluginSettingsItem,
> = [T] extends [{ type: 'boolean' }]
  ? boolean
  : [T] extends [{ type: 'choice' }]
      ? T['options'][number]['value']
      : [T] extends [{ type: 'text' }]
          ? string
          : unknown

export type ExtractSettingsTypes<
  O extends Record<string, PluginSettingsItem>,
> = {
  [K in keyof O]: InferSettingsType<O[K]>
}


================================================
FILE: packages/api/src/proxy.ts
================================================
import type { Context, DevtoolsPluginApi, Hookable } from './api/index.js'
import type { PluginDescriptor } from './plugin.js'
import { HOOK_PLUGIN_SETTINGS_SET } from './const.js'
import { now } from './time.js'

interface QueueItem {
  method: string
  args: any[]
  resolve?: (value?: any) => void
}

export class ApiProxy<TTarget extends DevtoolsPluginApi<any> = DevtoolsPluginApi<any>> {
  target: TTarget | null
  targetQueue: QueueItem[]
  proxiedTarget: TTarget

  onQueue: QueueItem[]
  proxiedOn: Hookable<Context>

  plugin: PluginDescriptor
  hook: any
  fallbacks: Record<string, any>

  constructor(plugin: PluginDescriptor, hook: any) {
    this.target = null
    this.targetQueue = []
    this.onQueue = []

    this.plugin = plugin
    this.hook = hook

    const defaultSettings: Record<string, any> = {}
    if (plugin.settings) {
      for (const id in plugin.settings) {
        const item = plugin.settings[id]
        defaultSettings[id] = item.defaultValue
      }
    }
    const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`
    let currentSettings = Object.assign({}, defaultSettings)
    try {
      const raw = localStorage.getItem(localSettingsSaveId)
      const data = JSON.parse(raw)
      Object.assign(currentSettings, data)
    }
    catch (e) {
      // noop
    }

    this.fallbacks = {
      getSettings() {
        return currentSettings
      },
      setSettings(value) {
        try {
          localStorage.setItem(localSettingsSaveId, JSON.stringify(value))
        }
        catch (e) {
          // noop
        }
        currentSettings = value
      },
      now() {
        return now()
      },
    }

    if (hook) {
      hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
        if (pluginId === this.plugin.id) {
          this.fallbacks.setSettings(value)
        }
      })
    }

    this.proxiedOn = new Proxy({} as Hookable<Context>, {
      get: (_target, prop: string) => {
        if (this.target) {
          return this.target.on[prop]
        }
        else {
          return (...args) => {
            this.onQueue.push({
              method: prop,
              args,
            })
          }
        }
      },
    })

    this.proxiedTarget = new Proxy({} as TTarget, {
      get: (_target, prop: string) => {
        if (this.target) {
          return this.target[prop]
        }
        else if (prop === 'on') {
          return this.proxiedOn
        }
        else if (Object.keys(this.fallbacks).includes(prop)) {
          return (...args) => {
            this.targetQueue.push({
              method: prop,
              args,
              resolve: () => { /* noop */ },
            })
            return this.fallbacks[prop](...args)
          }
        }
        else {
          return (...args) => {
            return new Promise((resolve) => {
              this.targetQueue.push({
                method: prop,
                args,
                resolve,
              })
            })
          }
        }
      },
    })
  }

  async setRealTarget(target: TTarget) {
    this.target = target

    for (const item of this.onQueue) {
      this.target.on[item.method](...item.args)
    }

    for (const item of this.targetQueue) {
      item.resolve(await this.target[item.method](...item.args))
    }
  }
}


================================================
FILE: packages/api/src/time.ts
================================================
let supported: boolean
let perf: Performance

export function isPerformanceSupported() {
  if (supported !== undefined) {
    return supported
  }
  if (typeof window !== 'undefined' && window.performance) {
    supported = true
    perf = window.performance
  }
  else if (typeof globalThis !== 'undefined' && (globalThis as any).perf_hooks?.performance) {
    supported = true
    perf = (globalThis as any).perf_hooks.performance
  }
  else {
    supported = false
  }
  return supported
}

export function now() {
  return isPerformanceSupported() ? perf.now() : Date.now()
}


================================================
FILE: packages/api/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["ESNext", "DOM"],
    "module": "ESNext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "types": ["node", "webpack-env"],
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "alwaysStrict": true,
    // Strict
    "noImplicitAny": false,
    "noImplicitThis": true,
    "removeComments": false,
    "sourceMap": false,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "preserveWatchOutput": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}


================================================
FILE: packages/app-backend-api/package.json
================================================
{
  "name": "@vue-devtools/app-backend-api",
  "version": "0.0.0",
  "private": true,
  "main": "./lib/index.js",
  "types": "./lib/index.d.ts",
  "scripts": {
    "build": "rimraf lib && yarn ts",
    "build:watch": "yarn ts -w",
    "ts": "tsc -d -outDir lib"
  },
  "dependencies": {
    "@vue-devtools/shared-utils": "^0.0.0",
    "@vue/devtools-api": "^6.0.0-beta.1"
  },
  "devDependencies": {
    "@types/node": "^20.11.16",
    "@types/webpack-env": "^1.15.1",
    "typescript": "^5.3.3"
  }
}


================================================
FILE: packages/app-backend-api/src/api.ts
================================================
import type {
  Bridge,
} from '@vue-devtools/shared-utils'
import {
  HookEvents,
  PluginPermission,
  StateEditor,
  getPluginDefaultSettings,
  getPluginSettings,
  hasPluginPermission,
  setPluginSettings,
} from '@vue-devtools/shared-utils'
import type {
  App,
  ComponentDevtoolsOptions,
  ComponentInstance,
  ComponentTreeNode,
  CustomInspectorOptions,
  DevtoolsPluginApi,
  EditStatePayload,
  HookPayloads,
  TimelineEventOptions,
  TimelineLayerOptions,
  WithId,
} from '@vue/devtools-api'
import {
  Hooks,
  now,
} from '@vue/devtools-api'
import { DevtoolsHookable } from './hooks'
import type { BackendContext } from './backend-context'
import type { Plugin } from './plugin'
import type { DevtoolsBackend } from './backend'
import type { AppRecord } from './app-record'

const pluginOn: DevtoolsHookable[] = []

export class DevtoolsApi {
  bridge: Bridge
  ctx: BackendContext
  backend: DevtoolsBackend
  on: DevtoolsHookable
  stateEditor: StateEditor = new StateEditor()

  constructor(backend: DevtoolsBackend, ctx: BackendContext) {
    this.backend = backend
    this.ctx = ctx
    this.bridge = ctx.bridge
    this.on = new DevtoolsHookable(ctx)
  }

  async callHook<T extends Hooks>(eventType: T, payload: HookPayloads[T], ctx: BackendContext = this.ctx) {
    payload = await this.on.callHandlers(eventType, payload, ctx)
    for (const on of pluginOn) {
      payload = await on.callHandlers(eventType, payload, ctx)
    }
    return payload
  }

  async transformCall(callName: string, ...args) {
    const payload = await this.callHook(Hooks.TRANSFORM_CALL, {
      callName,
      inArgs: args,
      outArgs: args.slice(),
    })
    return payload.outArgs
  }

  async getAppRecordName(app: App, defaultName: string): Promise<string> {
    const payload = await this.callHook(Hooks.GET_APP_RECORD_NAME, {
      app,
      name: null,
    })
    if (payload.name) {
      return payload.name
    }
    else {
      return `App ${defaultName}`
    }
  }

  async getAppRootInstance(app: App) {
    const payload = await this.callHook(Hooks.GET_APP_ROOT_INSTANCE, {
      app,
      root: null,
    })
    return payload.root
  }

  async registerApplication(app: App) {
    await this.callHook(Hooks.REGISTER_APPLICATION, {
      app,
    })
  }

  async walkComponentTree(instance: ComponentInstance, maxDepth = -1, filter: string = null, recursively = false) {
    const payload = await this.callHook(Hooks.WALK_COMPONENT_TREE, {
      componentInstance: instance,
      componentTreeData: null,
      maxDepth,
      filter,
      recursively,
    })
    return payload.componentTreeData
  }

  async visitComponentTree(instance: ComponentInstance, treeNode: ComponentTreeNode, filter: string = null, app: App) {
    const payload = await this.callHook(Hooks.VISIT_COMPONENT_TREE, {
      app,
      componentInstance: instance,
      treeNode,
      filter,
    })
    return payload.treeNode
  }

  async walkComponentParents(instance: ComponentInstance) {
    const payload = await this.callHook(Hooks.WALK_COMPONENT_PARENTS, {
      componentInstance: instance,
      parentInstances: [],
    })
    return payload.parentInstances
  }

  async inspectComponent(instance: ComponentInstance, app: App) {
    const payload = await this.callHook(Hooks.INSPECT_COMPONENT, {
      app,
      componentInstance: instance,
      instanceData: null,
    })
    return payload.instanceData
  }

  async getComponentBounds(instance: ComponentInstance) {
    const payload = await this.callHook(Hooks.GET_COMPONENT_BOUNDS, {
      componentInstance: instance,
      bounds: null,
    })
    return payload.bounds
  }

  async getComponentName(instance: ComponentInstance) {
    const payload = await this.callHook(Hooks.GET_COMPONENT_NAME, {
      componentInstance: instance,
      name: null,
    })
    return payload.name
  }

  async getComponentInstances(app: App) {
    const payload = await this.callHook(Hooks.GET_COMPONENT_INSTANCES, {
      app,
      componentInstances: [],
    })
    return payload.componentInstances
  }

  async getElementComponent(element: HTMLElement | any) {
    const payload = await this.callHook(Hooks.GET_ELEMENT_COMPONENT, {
      element,
      componentInstance: null,
    })
    return payload.componentInstance
  }

  async getComponentRootElements(instance: ComponentInstance) {
    const payload = await this.callHook(Hooks.GET_COMPONENT_ROOT_ELEMENTS, {
      componentInstance: instance,
      rootElements: [],
    })
    return payload.rootElements
  }

  async editComponentState(instance: ComponentInstance, dotPath: string, type: string, state: EditStatePayload, app: App) {
    const arrayPath = dotPath.split('.')
    const payload = await this.callHook(Hooks.EDIT_COMPONENT_STATE, {
      app,
      componentInstance: instance,
      path: arrayPath,
      type,
      state,
      set: (object, path = arrayPath, value = state.value, cb?) => this.stateEditor.set(object, path, value, cb || this.stateEditor.createDefaultSetCallback(state)),
    })
    return payload.componentInstance
  }

  async getComponentDevtoolsOptions(instance: ComponentInstance): Promise<ComponentDevtoolsOptions> {
    const payload = await this.callHook(Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS, {
      componentInstance: instance,
      options: null,
    })
    return payload.options || {}
  }

  async getComponentRenderCode(instance: ComponentInstance): Promise<{
    code: string
  }> {
    const payload = await this.callHook(Hooks.GET_COMPONENT_RENDER_CODE, {
      componentInstance: instance,
      code: null,
    })
    return {
      code: payload.code,
    }
  }

  async inspectTimelineEvent(eventData: TimelineEventOptions & WithId, app: App) {
    const payload = await this.callHook(Hooks.INSPECT_TIMELINE_EVENT, {
      event: eventData.event,
      layerId: eventData.layerId,
      app,
      data: eventData.event.data,
      all: eventData.all,
    })
    return payload.data
  }

  async clearTimeline() {
    await this.callHook(Hooks.TIMELINE_CLEARED, {})
  }

  async getInspectorTree(inspectorId: string, app: App, filter: string) {
    const payload = await this.callHook(Hooks.GET_INSPECTOR_TREE, {
      inspectorId,
      app,
      filter,
      rootNodes: [],
    })
    return payload.rootNodes
  }

  async getInspectorState(inspectorId: string, app: App, nodeId: string) {
    const payload = await this.callHook(Hooks.GET_INSPECTOR_STATE, {
      inspectorId,
      app,
      nodeId,
      state: null,
    })
    return payload.state
  }

  async editInspectorState(inspectorId: string, app: App, nodeId: string, dotPath: string, type: string, state: EditStatePayload) {
    const arrayPath = dotPath.split('.')
    await this.callHook(Hooks.EDIT_INSPECTOR_STATE, {
      inspectorId,
      app,
      nodeId,
      path: arrayPath,
      type,
      state,
      set: (object, path = arrayPath, value = state.value, cb?) => this.stateEditor.set(object, path, value, cb || this.stateEditor.createDefaultSetCallback(state)),
    })
  }

  now() {
    return now()
  }
}

export class DevtoolsPluginApiInstance<TSettings = any> implements DevtoolsPluginApi<TSettings> {
  bridge: Bridge
  ctx: BackendContext
  plugin: Plugin
  appRecord: AppRecord
  backendApi: DevtoolsApi
  on: DevtoolsHookable
  private defaultSettings: TSettings

  constructor(plugin: Plugin, appRecord: AppRecord, ctx: BackendContext) {
    this.bridge = ctx.bridge
    this.ctx = ctx
    this.plugin = plugin
    this.appRecord = appRecord
    this.backendApi = appRecord.backend.api
    this.defaultSettings = getPluginDefaultSettings(plugin.descriptor.settings)
    this.on = new DevtoolsHookable(ctx, plugin)
    pluginOn.push(this.on)
  }

  // Plugin API

  async notifyComponentUpdate(instance: ComponentInstance = null) {
    if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) {
      return
    }

    if (instance) {
      this.ctx.hook.emit(HookEvents.COMPONENT_UPDATED, ...await this.backendApi.transformCall(HookEvents.COMPONENT_UPDATED, instance))
    }
    else {
      this.ctx.hook.emit(HookEvents.COMPONENT_UPDATED)
    }
  }

  addTimelineLayer(options: TimelineLayerOptions) {
    if (!this.enabled || !this.hasPermission(PluginPermission.TIMELINE)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.TIMELINE_LAYER_ADDED, options, this.plugin)
    return true
  }

  addTimelineEvent(options: TimelineEventOptions) {
    if (!this.enabled || !this.hasPermission(PluginPermission.TIMELINE)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.TIMELINE_EVENT_ADDED, options, this.plugin)
    return true
  }

  addInspector(options: CustomInspectorOptions) {
    if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_ADD, options, this.plugin)
    return true
  }

  sendInspectorTree(inspectorId: string) {
    if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_SEND_TREE, inspectorId, this.plugin)
    return true
  }

  sendInspectorState(inspectorId: string) {
    if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_SEND_STATE, inspectorId, this.plugin)
    return true
  }

  selectInspectorNode(inspectorId: string, nodeId: string) {
    if (!this.enabled || !this.hasPermission(PluginPermission.CUSTOM_INSPECTOR)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.CUSTOM_INSPECTOR_SELECT_NODE, inspectorId, nodeId, this.plugin)
    return true
  }

  getComponentBounds(instance: ComponentInstance) {
    return this.backendApi.getComponentBounds(instance)
  }

  getComponentName(instance: ComponentInstance) {
    return this.backendApi.getComponentName(instance)
  }

  getComponentInstances(app: App) {
    return this.backendApi.getComponentInstances(app)
  }

  highlightElement(instance: ComponentInstance) {
    if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.COMPONENT_HIGHLIGHT, instance.__VUE_DEVTOOLS_UID__, this.plugin)
    return true
  }

  unhighlightElement() {
    if (!this.enabled || !this.hasPermission(PluginPermission.COMPONENTS)) {
      return false
    }

    this.ctx.hook.emit(HookEvents.COMPONENT_UNHIGHLIGHT, this.plugin)
    return true
  }

  getSettings(pluginId?: string) {
    return getPluginSettings(pluginId ?? this.plugin.descriptor.id, this.defaultSettings)
  }

  setSettings(value: TSettings, pluginId?: string) {
    setPluginSettings(pluginId ?? this.plugin.descriptor.id, value)
  }

  now() {
    return now()
  }

  private get enabled() {
    return hasPluginPermission(this.plugin.descriptor.id, PluginPermission.ENABLED)
  }

  private hasPermission(permission: PluginPermission) {
    return hasPluginPermission(this.plugin.descriptor.id, permission)
  }
}


================================================
FILE: packages/app-backend-api/src/app-record.ts
================================================
import type { App, ComponentInstance } from '@vue/devtools-api'
import type { DevtoolsBackend } from './backend'

export interface AppRecordOptions {
  app: App
  version: string
  types: { [key: string]: string | symbol }
  meta?: any
}

export interface AppRecord {
  id: string
  name: string
  options: AppRecordOptions
  backend: DevtoolsBackend
  lastInspectedComponentId: string
  instanceMap: Map<string, ComponentInstance>
  rootInstance: ComponentInstance
  componentFilter?: string
  perfGroupIds: Map<string, { groupId: number, time: number }>
  iframe: string
  meta: any
  missingInstanceQueue: Set<string>
}

/**
 * Used in the frontend
 */
export interface SimpleAppRecord {
  id: string
  name: string
  version: string
  iframe: string
}


================================================
FILE: packages/app-backend-api/src/backend-context.ts
================================================
import type { Bridge } from '@vue-devtools/shared-utils'
import type {
  CustomInspectorOptions,
  ID,
  TimelineEventOptions,
  TimelineLayerOptions,
  TimelineMarkerOptions,
  WithId,
} from '@vue/devtools-api'
import type { AppRecord } from './app-record'
import type { Plugin } from './plugin'
import type { DevtoolsHook } from './global-hook'
import type { DevtoolsBackend } from './backend'

export interface BackendContext {
  bridge: Bridge
  hook: DevtoolsHook
  backends: DevtoolsBackend[]
  appRecords: AppRecord[]
  currentTab: string
  currentAppRecord: AppRecord
  currentInspectedComponentId: string
  plugins: Plugin[]
  currentPlugin: Plugin
  timelineLayers: TimelineLayer[]
  nextTimelineEventId: number
  timelineEventMap: Map<ID, TimelineEventOptions & WithId>
  perfUniqueGroupId: number
  customInspectors: CustomInspector[]
  timelineMarkers: TimelineMarker[]
}

export interface TimelineLayer extends TimelineLayerOptions {
  appRecord: AppRecord | null
  plugin: Plugin
  events: (TimelineEventOptions & WithId)[]
}

export interface TimelineMarker extends TimelineMarkerOptions {
  appRecord: AppRecord | null
}

export interface CustomInspector extends CustomInspectorOptions {
  appRecord: AppRecord
  plugin: Plugin
  treeFilter: string
  selectedNodeId: string
}

export interface CreateBackendContextOptions {
  bridge: Bridge
  hook: DevtoolsHook
}

export function createBackendContext(options: CreateBackendContextOptions): BackendContext {
  return {
    bridge: options.bridge,
    hook: options.hook,
    backends: [],
    appRecords: [],
    currentTab: null,
    currentAppRecord: null,
    currentInspectedComponentId: null,
    plugins: [],
    currentPlugin: null,
    timelineLayers: [],
    nextTimelineEventId: 0,
    timelineEventMap: new Map(),
    perfUniqueGroupId: 0,
    customInspectors: [],
    timelineMarkers: [],
  }
}


================================================
FILE: packages/app-backend-api/src/backend.ts
================================================
import type { AppRecord } from './app-record'
import { DevtoolsApi } from './api'
import type { BackendContext } from './backend-context'

export enum BuiltinBackendFeature {
  /**
   * @deprecated
   */
  FLUSH = 'flush',
}

export interface DevtoolsBackendOptions {
  frameworkVersion: 1 | 2 | 3
  features: (BuiltinBackendFeature | string)[]
  setup: (api: DevtoolsApi) => void
  setupApp?: (api: DevtoolsApi, app: AppRecord) => void
}

export function defineBackend(options: DevtoolsBackendOptions) {
  return options
}

export interface DevtoolsBackend {
  options: DevtoolsBackendOptions
  api: DevtoolsApi
}

export function createBackend(options: DevtoolsBackendOptions, ctx: BackendContext): DevtoolsBackend {
  const backend: DevtoolsBackend = {
    options,
    api: null,
  }
  backend.api = new DevtoolsApi(backend, ctx)
  options.setup(backend.api)
  return backend
}


================================================
FILE: packages/app-backend-api/src/global-hook.ts
================================================
import type { AppRecordOptions } from './app-record'

export interface DevtoolsHook {
  emit: (event: string, ...payload: any[]) => void
  on: <T extends Function>(event: string, handler: T) => void
  once: <T extends Function>(event: string, handler: T) => void
  off: <T extends Function>(event?: string, handler?: T) => void
  Vue?: any
  apps: AppRecordOptions[]
}


================================================
FILE: packages/app-backend-api/src/hooks.ts
================================================
import { PluginPermission, SharedData, hasPluginPermission } from '@vue-devtools/shared-utils'
import type { HookHandler, HookPayloads, Hookable } from '@vue/devtools-api'
import { Hooks } from '@vue/devtools-api'
import type { BackendContext } from './backend-context'
import type { Plugin } from './plugin'

type Handler<TPayload> = HookHandler<TPayload, BackendContext>

export interface HookHandlerData<THandlerPayload> {
  handler: Handler<THandlerPayload>
  plugin: Plugin
}

export class DevtoolsHookable implements Hookable<BackendContext> {
  private handlers: Partial<{ [eventType in Hooks]: HookHandlerData<HookPayloads[eventType]>[] }> = {}
  private ctx: BackendContext
  private plugin: Plugin

  constructor(ctx: BackendContext, plugin: Plugin = null) {
    this.ctx = ctx
    this.plugin = plugin
  }

  private hook<T extends Hooks>(eventType: T, handler: Handler<HookPayloads[T]>, pluginPermision: PluginPermission = null) {
    const handlers = (this.handlers[eventType] = this.handlers[eventType] || []) as HookHandlerData<HookPayloads[T]>[]

    if (this.plugin) {
      const originalHandler = handler
      handler = (...args) => {
        // Plugin permission
        if (!hasPluginPermission(this.plugin.descriptor.id, PluginPermission.ENABLED)
          || (pluginPermision && !hasPluginPermission(this.plugin.descriptor.id, pluginPermision))
        ) { return }

        // App scope
        if (!this.plugin.descriptor.disableAppScope
          && this.ctx.currentAppRecord?.options.app !== this.plugin.descriptor.app) { return }

        // Plugin scope
        if (!this.plugin.descriptor.disablePluginScope
          && (args[0] as any).pluginId != null && (args[0] as any).pluginId !== this.plugin.descriptor.id) { return }

        return originalHandler(...args)
      }
    }

    handlers.push({
      handler,
      plugin: this.ctx.currentPlugin,
    })
  }

  async callHandlers<T extends Hooks>(eventType: T, payload: HookPayloads[T], ctx: BackendContext) {
    if (this.handlers[eventType]) {
      const handlers = this.handlers[eventType] as HookHandlerData<HookPayloads[T]>[]
      for (let i = 0; i < handlers.length; i++) {
        const { handler, plugin } = handlers[i]
        try {
          await handler(payload, ctx)
        }
        catch (e) {
          if (SharedData.debugInfo) {
            console.error(`An error occurred in hook '${eventType}'${plugin ? ` registered by plugin '${plugin.descriptor.id}'` : ''} with payload:`, payload)
            console.error(e)
          }
        }
      }
    }
    return payload
  }

  transformCall(handler: Handler<HookPayloads[Hooks.TRANSFORM_CALL]>) {
    this.hook(Hooks.TRANSFORM_CALL, handler)
  }

  getAppRecordName(handler: Handler<HookPayloads[Hooks.GET_APP_RECORD_NAME]>) {
    this.hook(Hooks.GET_APP_RECORD_NAME, handler)
  }

  getAppRootInstance(handler: Handler<HookPayloads[Hooks.GET_APP_ROOT_INSTANCE]>) {
    this.hook(Hooks.GET_APP_ROOT_INSTANCE, handler)
  }

  registerApplication(handler: Handler<HookPayloads[Hooks.REGISTER_APPLICATION]>) {
    this.hook(Hooks.REGISTER_APPLICATION, handler)
  }

  walkComponentTree(handler: Handler<HookPayloads[Hooks.WALK_COMPONENT_TREE]>) {
    this.hook(Hooks.WALK_COMPONENT_TREE, handler, PluginPermission.COMPONENTS)
  }

  visitComponentTree(handler: Handler<HookPayloads[Hooks.VISIT_COMPONENT_TREE]>) {
    this.hook(Hooks.VISIT_COMPONENT_TREE, handler, PluginPermission.COMPONENTS)
  }

  walkComponentParents(handler: Handler<HookPayloads[Hooks.WALK_COMPONENT_PARENTS]>) {
    this.hook(Hooks.WALK_COMPONENT_PARENTS, handler, PluginPermission.COMPONENTS)
  }

  inspectComponent(handler: Handler<HookPayloads[Hooks.INSPECT_COMPONENT]>) {
    this.hook(Hooks.INSPECT_COMPONENT, handler, PluginPermission.COMPONENTS)
  }

  getComponentBounds(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_BOUNDS]>) {
    this.hook(Hooks.GET_COMPONENT_BOUNDS, handler, PluginPermission.COMPONENTS)
  }

  getComponentName(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_NAME]>) {
    this.hook(Hooks.GET_COMPONENT_NAME, handler, PluginPermission.COMPONENTS)
  }

  getComponentInstances(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_INSTANCES]>) {
    this.hook(Hooks.GET_COMPONENT_INSTANCES, handler, PluginPermission.COMPONENTS)
  }

  getElementComponent(handler: Handler<HookPayloads[Hooks.GET_ELEMENT_COMPONENT]>) {
    this.hook(Hooks.GET_ELEMENT_COMPONENT, handler, PluginPermission.COMPONENTS)
  }

  getComponentRootElements(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_ROOT_ELEMENTS]>) {
    this.hook(Hooks.GET_COMPONENT_ROOT_ELEMENTS, handler, PluginPermission.COMPONENTS)
  }

  editComponentState(handler: Handler<HookPayloads[Hooks.EDIT_COMPONENT_STATE]>) {
    this.hook(Hooks.EDIT_COMPONENT_STATE, handler, PluginPermission.COMPONENTS)
  }

  getComponentDevtoolsOptions(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS]>) {
    this.hook(Hooks.GET_COMPONENT_DEVTOOLS_OPTIONS, handler, PluginPermission.COMPONENTS)
  }

  getComponentRenderCode(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_RENDER_CODE]>) {
    this.hook(Hooks.GET_COMPONENT_RENDER_CODE, handler, PluginPermission.COMPONENTS)
  }

  inspectTimelineEvent(handler: Handler<HookPayloads[Hooks.INSPECT_TIMELINE_EVENT]>) {
    this.hook(Hooks.INSPECT_TIMELINE_EVENT, handler, PluginPermission.TIMELINE)
  }

  timelineCleared(handler: Handler<HookPayloads[Hooks.TIMELINE_CLEARED]>) {
    this.hook(Hooks.TIMELINE_CLEARED, handler, PluginPermission.TIMELINE)
  }

  getInspectorTree(handler: Handler<HookPayloads[Hooks.GET_INSPECTOR_TREE]>) {
    this.hook(Hooks.GET_INSPECTOR_TREE, handler, PluginPermission.CUSTOM_INSPECTOR)
  }

  getInspectorState(handler: Handler<HookPayloads[Hooks.GET_INSPECTOR_STATE]>) {
    this.hook(Hooks.GET_INSPECTOR_STATE, handler, PluginPermission.CUSTOM_INSPECTOR)
  }

  editInspectorState(handler: Handler<HookPayloads[Hooks.EDIT_INSPECTOR_STATE]>) {
    this.hook(Hooks.EDIT_INSPECTOR_STATE, handler, PluginPermission.CUSTOM_INSPECTOR)
  }

  setPluginSettings(handler: Handler<HookPayloads[Hooks.SET_PLUGIN_SETTINGS]>) {
    this.hook(Hooks.SET_PLUGIN_SETTINGS, handler)
  }
}


================================================
FILE: packages/app-backend-api/src/index.ts
================================================
export * from './api'
export * from './app-record'
export * from './backend'
export * from './backend-context'
export * from './global-hook'
export * from './hooks'
export * from './plugin'


================================================
FILE: packages/app-backend-api/src/plugin.ts
================================================
import type { PluginDescriptor, SetupFunction } from '@vue/devtools-api'

export interface Plugin {
  descriptor: PluginDescriptor
  setupFn: SetupFunction
  error: Error
}


================================================
FILE: packages/app-backend-api/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "ES2019",
    "module": "commonjs",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "types": [
      "node",
      "webpack-env"
    ],
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "alwaysStrict": true,
    // Strict
    "noImplicitAny": false,
    "noImplicitThis": true,
    "sourceMap": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "preserveWatchOutput": true
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}


================================================
FILE: packages/app-backend-core/package.json
================================================
{
  "name": "@vue-devtools/app-backend-core",
  "version": "0.0.0",
  "private": true,
  "main": "./lib/index.js",
  "types": "./lib/index.d.ts",
  "scripts": {
    "build": "rimraf lib && yarn ts",
    "build:watch": "yarn ts -w",
    "ts": "tsc -d -outDir lib"
  },
  "dependencies": {
    "@vue-devtools/app-backend-api": "^0.0.0",
    "@vue-devtools/app-backend-vue1": "^0.0.0",
    "@vue-devtools/app-backend-vue2": "^0.0.0",
    "@vue-devtools/app-backend-vue3": "^0.0.0",
    "@vue-devtools/shared-utils": "^0.0.0",
    "@vue/devtools-api": "^6.0.0-beta.1",
    "lodash": "^4.17.21",
    "speakingurl": "^14.0.1"
  },
  "devDependencies": {
    "@types/node": "^20.11.16",
    "@types/webpack-env": "^1.15.1",
    "typescript": "^5.3.3"
  }
}


================================================
FILE: packages/app-backend-core/src/app.ts
================================================
import type {
  AppRecord,
  AppRecordOptions,
  BackendContext,
  DevtoolsBackend,
  SimpleAppRecord,
} from '@vue-devtools/app-backend-api'
import { BridgeEvents, SharedData, isBrowser } from '@vue-devtools/shared-utils'
import type { App } from '@vue/devtools-api'
import slug from 'speakingurl'
import { JobQueue } from './util/queue'
import { scan } from './legacy/scan'
import { addBuiltinLayers, removeLayersForApp } from './timeline'
import { availableBackends, getBackend } from './backend'
import { hook } from './global-hook.js'
import { sendComponentTreeData, sendSelectedComponentData } from './component.js'

const jobs = new JobQueue()

let recordId = 0

type AppRecordResolver = (record: AppRecord) => void | Promise<void>
const appRecordPromises = new Map<App, AppRecordResolver[]>()

export async function registerApp(options: AppRecordOptions, ctx: BackendContext) {
  return jobs.queue('regiserApp', () => registerAppJob(options, ctx))
}

async function registerAppJob(options: AppRecordOptions, ctx: BackendContext) {
  // Dedupe
  if (ctx.appRecords.find(a => a.options.app === options.app)) {
    return
  }

  if (!options.version) {
    throw new Error('[Vue Devtools] Vue version not found')
  }

  // Find correct backend
  const baseFrameworkVersion = Number.parseInt(options.version.substring(0, options.version.indexOf('.')))
  for (let i = 0; i < availableBackends.length; i++) {
    const backendOptions = availableBackends[i]
    if (backendOptions.frameworkVersion === baseFrameworkVersion) {
      // Enable backend if it's not enabled
      const backend = getBackend(backendOptions, ctx)

      await createAppRecord(options, backend, ctx)

      break
    }
  }
}

async function createAppRecord(options: AppRecordOptions, backend: DevtoolsBackend, ctx: BackendContext) {
  const rootInstance = await backend.api.getAppRootInstance(options.app)
  if (rootInstance) {
    if ((await backend.api.getComponentDevtoolsOptions(rootInstance)).hide) {
      options.app._vueDevtools_hidden_ = true
      return
    }

    recordId++
    const name = await backend.api.getAppRecordName(options.app, recordId.toString())
    const id = getAppRecordId(options.app, slug(name))

    const [el]: HTMLElement[] = await backend.api.getComponentRootElements(rootInstance)

    const instanceMapRaw = new Map<string, any>()

    const record: AppRecord = {
      id,
      name,
      options,
      backend,
      lastInspectedComponentId: null,
      instanceMap: new Proxy(instanceMapRaw, {
        get(target, key: string) {
          if (key === 'set') {
            return (instanceId: string, instance: any) => {
              target.set(instanceId, instance)
              // The component was requested by the frontend before it was registered
              if (record.missingInstanceQueue.has(instanceId)) {
                record.missingInstanceQueue.delete(instanceId)
                if (ctx.currentAppRecord === record) {
                  sendComponentTreeData(record, instanceId, record.componentFilter, null, false, ctx)
                  if (record.lastInspectedComponentId === instanceId) {
                    sendSelectedComponentData(record, instanceId, ctx)
                  }
                }
              }
            }
          }
          return target[key].bind(target)
        },
      }),
      rootInstance,
      perfGroupIds: new Map(),
      iframe: isBrowser && document !== el?.ownerDocument ? el?.ownerDocument?.location?.pathname : null,
      meta: options.meta ?? {},
      missingInstanceQueue: new Set(),
    }

    options.app.__VUE_DEVTOOLS_APP_RECORD__ = record
    const rootId = `${record.id}:root`
    record.instanceMap.set(rootId, record.rootInstance)
    record.rootInstance.__VUE_DEVTOOLS_UID__ = rootId

    // Timeline
    addBuiltinLayers(record, ctx)

    ctx.appRecords.push(record)

    if (backend.options.setupApp) {
      backend.options.setupApp(backend.api, record)
    }

    await backend.api.registerApplication(options.app)

    ctx.bridge.send(BridgeEvents.TO_FRONT_APP_ADD, {
      appRecord: mapAppRecord(record),
    })

    // Auto select first app
    if (ctx.currentAppRecord == null) {
      await selectApp(record, ctx)
    }

    if (appRecordPromises.has(options.app)) {
      for (const r of appRecordPromises.get(options.app)) {
        await r(record)
      }
    }
  }
  else if (SharedData.debugInfo) {
    console.warn('[Vue devtools] No root instance found for app, it might have been unmounted', options.app)
  }
}

export async function selectApp(record: AppRecord, ctx: BackendContext) {
  ctx.currentAppRecord = record
  ctx.currentInspectedComponentId = record.lastInspectedComponentId
  ctx.bridge.send(BridgeEvents.TO_FRONT_APP_SELECTED, {
    id: record.id,
    lastInspectedComponentId: record.lastInspectedComponentId,
  })
}

export function mapAppRecord(record: AppRecord): SimpleAppRecord {
  return {
    id: record.id,
    name: record.name,
    version: record.options.version,
    iframe: record.iframe,
  }
}

const appIds = new Set()

export function getAppRecordId(app, defaultId?: string): string {
  if (app.__VUE_DEVTOOLS_APP_RECORD_ID__ != null) {
    return app.__VUE_DEVTOOLS_APP_RECORD_ID__
  }
  let id = defaultId ?? (recordId++).toString()

  if (defaultId && appIds.has(id)) {
    let count = 1
    while (appIds.has(`${defaultId}_${count}`)) {
      count++
    }
    id = `${defaultId}_${count}`
  }

  appIds.add(id)

  app.__VUE_DEVTOOLS_APP_RECORD_ID__ = id
  return id
}

export async function getAppRecord(app: any, ctx: BackendContext): Promise<AppRecord> {
  const record = app.__VUE_DEVTOOLS_APP_RECORD__ ?? ctx.appRecords.find(ar => ar.options.app === app)
  if (record) {
    return record
  }
  if (app._vueDevtools_hidden_) {
    return null
  }
  return new Promise((resolve, reject) => {
    let resolvers = appRecordPromises.get(app)
    let timedOut = false
    if (!resolvers) {
      resolvers = []
      appRecordPromises.set(app, resolvers)
    }
    let timer: any
    const fn = (record) => {
      if (!timedOut) {
        clearTimeout(timer)
        resolve(record)
      }
    }
    resolvers.push(fn)
    timer = setTimeout(() => {
      timedOut = true
      const index = resolvers.indexOf(fn)
      if (index !== -1) {
        resolvers.splice(index, 1)
      }
      if (SharedData.debugInfo) {
        // eslint-disable-next-line no-console
        console.log('Timed out waiting for app record', app)
      }
      reject(new Error(`Timed out getting app record for app`))
    }, 60000)
  })
}

export function waitForAppsRegistration() {
  return jobs.queue('waitForAppsRegistrationNoop', async () => { /* NOOP */ })
}

export async function sendApps(ctx: BackendContext) {
  const appRecords = []

  for (const appRecord of ctx.appRecords) {
    appRecords.push(appRecord)
  }

  ctx.bridge.send(BridgeEvents.TO_FRONT_APP_LIST, {
    apps: appRecords.map(mapAppRecord),
  })
}

function removeAppRecord(appRecord: AppRecord, ctx: BackendContext) {
  try {
    appIds.delete(appRecord.id)
    const index = ctx.appRecords.indexOf(appRecord)
    if (index !== -1) {
      ctx.appRecords.splice(index, 1)
    }
    removeLayersForApp(appRecord.options.app, ctx)
    ctx.bridge.send(BridgeEvents.TO_FRONT_APP_REMOVE, { id: appRecord.id })
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(e)
    }
  }
}

export async function removeApp(app: App, ctx: BackendContext) {
  try {
    const appRecord = await getAppRecord(app, ctx)
    if (appRecord) {
      removeAppRecord(appRecord, ctx)
    }
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(e)
    }
  }
}

let scanTimeout: any

export function _legacy_getAndRegisterApps(ctx: BackendContext, clear = false) {
  setTimeout(() => {
    try {
      if (clear) {
        // Remove apps that are legacy
        ctx.appRecords.forEach((appRecord) => {
          if (appRecord.meta.Vue) {
            removeAppRecord(appRecord, ctx)
          }
        })
      }

      const apps = scan()

      clearTimeout(scanTimeout)
      if (!apps.length) {
        scanTimeout = setTimeout(() => _legacy_getAndRegisterApps(ctx), 1000)
      }

      apps.forEach((app) => {
        const Vue = hook.Vue
        registerApp({
          app,
          types: {},
          version: Vue?.version,
          meta: {
            Vue,
          },
        }, ctx)
      })
    }
    catch (e) {
      if (SharedData.debugInfo) {
        console.error(`Error scanning for legacy apps:`)
        console.error(e)
      }
    }
  }, 0)
}


================================================
FILE: packages/app-backend-core/src/backend.ts
================================================
import type { BackendContext, DevtoolsBackend, DevtoolsBackendOptions } from '@vue-devtools/app-backend-api'
import { createBackend } from '@vue-devtools/app-backend-api'

import { backend as backendVue1 } from '@vue-devtools/app-backend-vue1'
import { backend as backendVue2 } from '@vue-devtools/app-backend-vue2'
import { backend as backendVue3 } from '@vue-devtools/app-backend-vue3'

import { handleAddPerformanceTag } from './perf'

export const availableBackends = [
  backendVue1,
  backendVue2,
  backendVue3,
]

const enabledBackends: Map<DevtoolsBackendOptions, DevtoolsBackend> = new Map()

export function getBackend(backendOptions: DevtoolsBackendOptions, ctx: BackendContext) {
  let backend: DevtoolsBackend
  if (!enabledBackends.has(backendOptions)) {
    // Create backend
    backend = createBackend(backendOptions, ctx)
    handleAddPerformanceTag(backend, ctx)
    enabledBackends.set(backendOptions, backend)
    ctx.backends.push(backend)
  }
  else {
    backend = enabledBackends.get(backendOptions)
  }
  return backend
}


================================================
FILE: packages/app-backend-core/src/component-pick.ts
================================================
import { BridgeEvents, isBrowser } from '@vue-devtools/shared-utils'
import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api'
import type { ComponentInstance } from '@vue/devtools-api'
import { highlight, unHighlight } from './highlighter'

export default class ComponentPicker {
  ctx: BackendContext
  selectedInstance: ComponentInstance
  selectedBackend: DevtoolsBackend

  constructor(ctx: BackendContext) {
    this.ctx = ctx
    this.bindMethods()
  }

  /**
   * Adds event listeners for mouseover and mouseup
   */
  startSelecting() {
    if (!isBrowser) {
      return
    }
    window.addEventListener('mouseover', this.elementMouseOver, true)
    window.addEventListener('click', this.elementClicked, true)
    window.addEventListener('mouseout', this.cancelEvent, true)
    window.addEventListener('mouseenter', this.cancelEvent, true)
    window.addEventListener('mouseleave', this.cancelEvent, true)
    window.addEventListener('mousedown', this.cancelEvent, true)
    window.addEventListener('mouseup', this.cancelEvent, true)
  }

  /**
   * Removes event listeners
   */
  stopSelecting() {
    if (!isBrowser) {
      return
    }
    window.removeEventListener('mouseover', this.elementMouseOver, true)
    window.removeEventListener('click', this.elementClicked, true)
    window.removeEventListener('mouseout', this.cancelEvent, true)
    window.removeEventListener('mouseenter', this.cancelEvent, true)
    window.removeEventListener('mouseleave', this.cancelEvent, true)
    window.removeEventListener('mousedown', this.cancelEvent, true)
    window.removeEventListener('mouseup', this.cancelEvent, true)

    unHighlight()
  }

  /**
   * Highlights a component on element mouse over
   */
  async elementMouseOver(e: MouseEvent) {
    this.cancelEvent(e)

    const el = e.target
    if (el) {
      await this.selectElementComponent(el)
    }

    unHighlight()
    if (this.selectedInstance) {
      highlight(this.selectedInstance, this.selectedBackend, this.ctx)
    }
  }

  async selectElementComponent(el) {
    for (const backend of this.ctx.backends) {
      const instance = await backend.api.getElementComponent(el)
      if (instance) {
        this.selectedInstance = instance
        this.selectedBackend = backend
        return
      }
    }
    this.selectedInstance = null
    this.selectedBackend = null
  }

  /**
   * Selects an instance in the component view
   */
  async elementClicked(e: MouseEvent) {
    this.cancelEvent(e)

    if (this.selectedInstance && this.selectedBackend) {
      const parentInstances = await this.selectedBackend.api.walkComponentParents(this.selectedInstance)
      this.ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_PICK, { id: this.selectedInstance.__VUE_DEVTOOLS_UID__, parentIds: parentInstances.map(i => i.__VUE_DEVTOOLS_UID__) })
    }
    else {
      this.ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_PICK_CANCELED, null)
    }

    this.stopSelecting()
  }

  /**
   * Cancel a mouse event
   */
  cancelEvent(e: MouseEvent) {
    e.stopImmediatePropagation()
    e.preventDefault()
  }

  /**
   * Bind class methods to the class scope to avoid rebind for event listeners
   */
  bindMethods() {
    this.startSelecting = this.startSelecting.bind(this)
    this.stopSelecting = this.stopSelecting.bind(this)
    this.elementMouseOver = this.elementMouseOver.bind(this)
    this.elementClicked = this.elementClicked.bind(this)
  }
}


================================================
FILE: packages/app-backend-core/src/component.ts
================================================
import { BridgeEvents, SharedData, createThrottleQueue, parse, stringify } from '@vue-devtools/shared-utils'
import type { AppRecord, BackendContext } from '@vue-devtools/app-backend-api'
import { BuiltinBackendFeature } from '@vue-devtools/app-backend-api'
import type { App, ComponentInstance, EditStatePayload } from '@vue/devtools-api'
import { getAppRecord } from './app'

const MAX_$VM = 10
const $vmQueue = []

export async function sendComponentTreeData(appRecord: AppRecord, instanceId: string, filter = '', maxDepth: number = null, recursively = false, ctx: BackendContext) {
  if (!instanceId || appRecord !== ctx.currentAppRecord) {
    return
  }

  // Flush will send all components in the tree
  // So we skip individiual tree updates
  if (
    instanceId !== '_root'
    && ctx.currentAppRecord.backend.options.features.includes(BuiltinBackendFeature.FLUSH)
  ) {
    return
  }

  const instance = getComponentInstance(appRecord, instanceId, ctx)
  if (!instance) {
    ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_TREE, {
      instanceId,
      treeData: null,
      notFound: true,
    })
  }
  else {
    if (filter) {
      filter = filter.toLowerCase()
    }
    if (maxDepth == null) {
      maxDepth = instance === ctx.currentAppRecord.rootInstance ? 2 : 1
    }
    const data = await appRecord.backend.api.walkComponentTree(instance, maxDepth, filter, recursively)
    const payload = {
      instanceId,
      treeData: stringify(data),
    }
    ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_TREE, payload)
  }
}

export async function sendSelectedComponentData(appRecord: AppRecord, instanceId: string, ctx: BackendContext) {
  if (!instanceId || appRecord !== ctx.currentAppRecord) {
    return
  }
  const instance = getComponentInstance(appRecord, instanceId, ctx)
  if (!instance) {
    sendEmptyComponentData(instanceId, ctx)
  }
  else {
    // Expose instance on window
    if (typeof window !== 'undefined') {
      const win = window as any
      win.$vm = instance

      // $vm0, $vm1, $vm2, ...
      if ($vmQueue[0] !== instance) {
        if ($vmQueue.length >= MAX_$VM) {
          $vmQueue.pop()
        }
        for (let i = $vmQueue.length; i > 0; i--) {
          win[`$vm${i}`] = $vmQueue[i] = $vmQueue[i - 1]
        }
        win.$vm0 = $vmQueue[0] = instance
      }
    }
    if (SharedData.debugInfo) {
      // eslint-disable-next-line no-console
      console.log('[DEBUG] inspect', instance)
    }
    const parentInstances = await appRecord.backend.api.walkComponentParents(instance)
    const payload = {
      instanceId,
      data: stringify(await appRecord.backend.api.inspectComponent(instance, ctx.currentAppRecord.options.app)),
      parentIds: parentInstances.map(i => i.__VUE_DEVTOOLS_UID__),
    }
    ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_SELECTED_DATA, payload)
    markSelectedInstance(instanceId, ctx)
  }
}

export function markSelectedInstance(instanceId: string, ctx: BackendContext) {
  ctx.currentInspectedComponentId = instanceId
  ctx.currentAppRecord.lastInspectedComponentId = instanceId
}

export function sendEmptyComponentData(instanceId: string, ctx: BackendContext) {
  ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_SELECTED_DATA, {
    instanceId,
    data: null,
  })
}

export async function editComponentState(instanceId: string, dotPath: string, type: string, state: EditStatePayload, ctx: BackendContext) {
  if (!instanceId) {
    return
  }
  const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx)
  if (instance) {
    if ('value' in state && state.value != null) {
      state.value = parse(state.value, true)
    }
    await ctx.currentAppRecord.backend.api.editComponentState(instance, dotPath, type, state, ctx.currentAppRecord.options.app)
    await sendSelectedComponentData(ctx.currentAppRecord, instanceId, ctx)
  }
}

export async function getComponentId(app: App, uid: number, instance: ComponentInstance, ctx: BackendContext) {
  try {
    if (instance.__VUE_DEVTOOLS_UID__) {
      return instance.__VUE_DEVTOOLS_UID__
    }
    const appRecord = await getAppRecord(app, ctx)
    if (!appRecord) {
      return null
    }
    const isRoot = appRecord.rootInstance === instance
    return `${appRecord.id}:${isRoot ? 'root' : uid}`
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(e)
    }
    return null
  }
}

export function getComponentInstance(appRecord: AppRecord, instanceId: string, _ctx: BackendContext) {
  if (instanceId === '_root') {
    instanceId = `${appRecord.id}:root`
  }
  const instance = appRecord.instanceMap.get(instanceId)
  if (!instance) {
    appRecord.missingInstanceQueue.add(instanceId)
    if (SharedData.debugInfo) {
      console.warn(`Instance uid=${instanceId} not found`)
    }
  }
  return instance
}

export async function refreshComponentTreeSearch(ctx: BackendContext) {
  if (!ctx.currentAppRecord.componentFilter) {
    return
  }
  await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx)
}

const updateTrackingQueue = createThrottleQueue(500)

export function sendComponentUpdateTracking(instanceId: string, time: number, ctx: BackendContext) {
  if (!instanceId) {
    return
  }

  updateTrackingQueue.add(instanceId, () => {
    const payload = {
      instanceId,
      time,
    }
    ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_UPDATED, payload)
  })
}


================================================
FILE: packages/app-backend-core/src/flash.ts
================================================
import type { DevtoolsBackend } from '@vue-devtools/app-backend-api'
import type { ComponentInstance } from '@vue/devtools-api'

export async function flashComponent(instance: ComponentInstance, backend: DevtoolsBackend) {
  const bounds = await backend.api.getComponentBounds(instance)
  if (bounds) {
    let overlay: HTMLDivElement = instance.__VUE_DEVTOOLS_FLASH
    if (!overlay) {
      overlay = document.createElement('div')
      instance.__VUE_DEVTOOLS_FLASH = overlay
      overlay.style.border = '2px rgba(65, 184, 131, 0.7) solid'
      overlay.style.position = 'fixed'
      overlay.style.zIndex = '99999999999998'
      overlay.style.pointerEvents = 'none'
      overlay.style.borderRadius = '3px'
      overlay.style.boxSizing = 'border-box'
      document.body.appendChild(overlay)
    }
    overlay.style.opacity = '1'
    overlay.style.transition = null
    overlay.style.width = `${Math.round(bounds.width)}px`
    overlay.style.height = `${Math.round(bounds.height)}px`
    overlay.style.left = `${Math.round(bounds.left)}px`
    overlay.style.top = `${Math.round(bounds.top)}px`
    requestAnimationFrame(() => {
      overlay.style.transition = 'opacity 1s'
      overlay.style.opacity = '0'
    })
    clearTimeout((overlay as any)._timer)
    ;(overlay as any)._timer = setTimeout(() => {
      document.body.removeChild(overlay)
      instance.__VUE_DEVTOOLS_FLASH = null
    }, 1000)
  }
}


================================================
FILE: packages/app-backend-core/src/global-hook.ts
================================================
import type { DevtoolsHook } from '@vue-devtools/app-backend-api'
import { target } from '@vue-devtools/shared-utils'

// hook should have been injected before this executes.
export const hook: DevtoolsHook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__


================================================
FILE: packages/app-backend-core/src/highlighter.ts
================================================
import { isBrowser } from '@vue-devtools/shared-utils'
import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api'
import type { ComponentBounds, ComponentInstance } from '@vue/devtools-api'
import { JobQueue } from './util/queue'

let overlay: HTMLDivElement
let overlayContent: HTMLDivElement
let currentInstance

function createOverlay() {
  if (overlay || !isBrowser) {
    return
  }
  overlay = document.createElement('div')
  overlay.style.backgroundColor = 'rgba(65, 184, 131, 0.35)'
  overlay.style.position = 'fixed'
  overlay.style.zIndex = '99999999999998'
  overlay.style.pointerEvents = 'none'
  overlay.style.borderRadius = '3px'
  overlayContent = document.createElement('div')
  overlayContent.style.position = 'fixed'
  overlayContent.style.zIndex = '99999999999999'
  overlayContent.style.pointerEvents = 'none'
  overlayContent.style.backgroundColor = 'white'
  overlayContent.style.fontFamily = 'monospace'
  overlayContent.style.fontSize = '11px'
  overlayContent.style.padding = '4px 8px'
  overlayContent.style.borderRadius = '3px'
  overlayContent.style.color = '#333'
  overlayContent.style.textAlign = 'center'
  overlayContent.style.border = 'rgba(65, 184, 131, 0.5) 1px solid'
  overlayContent.style.backgroundClip = 'padding-box'
}

// Use a job queue to preserve highlight/unhighlight calls order
// This prevents "sticky" highlights that are not removed because highlight is async
const jobQueue = new JobQueue()

export async function highlight(instance: ComponentInstance, backend: DevtoolsBackend, ctx: BackendContext) {
  await jobQueue.queue('highlight', async () => {
    if (!instance) {
      return
    }

    const bounds = await backend.api.getComponentBounds(instance)
    if (bounds) {
      createOverlay()

      // Name
      const name = (await backend.api.getComponentName(instance)) || 'Anonymous'
      const pre = document.createElement('span')
      pre.style.opacity = '0.6'
      pre.textContent = '<'
      const text = document.createElement('span')
      text.style.fontWeight = 'bold'
      text.style.color = '#09ab56'
      text.textContent = name
      const post = document.createElement('span')
      post.style.opacity = '0.6'
      post.textContent = '>'

      // Size
      const size = document.createElement('span')
      size.style.opacity = '0.5'
      size.style.marginLeft = '6px'
      size.appendChild(document.createTextNode((Math.round(bounds.width * 100) / 100).toString()))
      const multiply = document.createElement('span')
      multiply.style.marginLeft = multiply.style.marginRight = '2px'
      multiply.textContent = '×'
      size.appendChild(multiply)
      size.appendChild(document.createTextNode((Math.round(bounds.height * 100) / 100).toString()))

      currentInstance = instance

      await showOverlay(bounds, [pre, text, post, size])
    }

    startUpdateTimer(backend, ctx)
  })
}

export async function unHighlight() {
  await jobQueue.queue('unHighlight', async () => {
    overlay?.parentNode?.removeChild(overlay)
    overlayContent?.parentNode?.removeChild(overlayContent)
    currentInstance = null

    stopUpdateTimer()
  })
}

function showOverlay(bounds: ComponentBounds, children: Node[] = null) {
  if (!isBrowser || !children.length) {
    return
  }

  positionOverlay(bounds)
  document.body.appendChild(overlay)

  overlayContent.innerHTML = ''
  children.forEach(child => overlayContent.appendChild(child))
  document.body.appendChild(overlayContent)

  positionOverlayContent(bounds)
}

function positionOverlay({ width = 0, height = 0, top = 0, left = 0 }) {
  overlay.style.width = `${Math.round(width)}px`
  overlay.style.height = `${Math.round(height)}px`
  overlay.style.left = `${Math.round(left)}px`
  overlay.style.top = `${Math.round(top)}px`
}

function positionOverlayContent({ height = 0, top = 0, left = 0 }) {
  // Content position (prevents overflow)
  const contentWidth = overlayContent.offsetWidth
  const contentHeight = overlayContent.offsetHeight
  let contentLeft = left
  if (contentLeft < 0) {
    contentLeft = 0
  }
  else if (contentLeft + contentWidth > window.innerWidth) {
    contentLeft = window.innerWidth - contentWidth
  }
  let contentTop = top - contentHeight - 2
  if (contentTop < 0) {
    contentTop = top + height + 2
  }
  if (contentTop < 0) {
    contentTop = 0
  }
  else if (contentTop + contentHeight > window.innerHeight) {
    contentTop = window.innerHeight - contentHeight
  }
  overlayContent.style.left = `${~~contentLeft}px`
  overlayContent.style.top = `${~~contentTop}px`
}

async function updateOverlay(backend: DevtoolsBackend, _ctx: BackendContext) {
  if (currentInstance) {
    const bounds = await backend.api.getComponentBounds(currentInstance)
    if (bounds) {
      const sizeEl = overlayContent.children.item(3)
      const widthEl = sizeEl.childNodes[0] as unknown as Text
      widthEl.textContent = (Math.round(bounds.width * 100) / 100).toString()
      const heightEl = sizeEl.childNodes[2] as unknown as Text
      heightEl.textContent = (Math.round(bounds.height * 100) / 100).toString()

      positionOverlay(bounds)
      positionOverlayContent(bounds)
    }
  }
}

let updateTimer

function startUpdateTimer(backend: DevtoolsBackend, ctx: BackendContext) {
  stopUpdateTimer()
  updateTimer = setInterval(() => {
    jobQueue.queue('updateOverlay', async () => {
      await updateOverlay(backend, ctx)
    })
  }, 1000 / 30) // 30fps
}

function stopUpdateTimer() {
  clearInterval(updateTimer)
}


================================================
FILE: packages/app-backend-core/src/hook.ts
================================================
// this script is injected into every page.

/**
 * Install the hook on window, which is an event emitter.
 * Note because Chrome content scripts cannot directly modify the window object,
 * we are evaling this function by inserting a script tag. That's why we have
 * to inline the whole event emitter implementation here.
 *
 * @param {Window|global} target
 */
export function installHook(target, isIframe = false) {
  const devtoolsVersion = '6.0'
  let listeners = {}

  function injectIframeHook(iframe) {
    if ((iframe as any).__vdevtools__injected) {
      return
    }
    try {
      (iframe as any).__vdevtools__injected = true
      const inject = () => {
        try {
          (iframe.contentWindow as any).__VUE_DEVTOOLS_IFRAME__ = iframe
          const script = iframe.contentDocument.createElement('script')
          script.textContent = `;(${installHook.toString()})(window, true)`
          iframe.contentDocument.documentElement.appendChild(script)
          script.parentNode.removeChild(script)
        }
        catch (e) {
          // Ignore
        }
      }
      inject()
      iframe.addEventListener('load', () => inject())
    }
    catch (e) {
      // Ignore
    }
  }

  let iframeChecks = 0
  function injectToIframes() {
    if (typeof window === 'undefined') {
      return
    }

    const iframes = document.querySelectorAll<HTMLIFrameElement>('iframe:not([data-vue-devtools-ignore])')
    for (const iframe of iframes) {
      injectIframeHook(iframe)
    }
  }
  injectToIframes()
  const iframeTimer = setInterval(() => {
    injectToIframes()
    iframeChecks++
    if (iframeChecks >= 5) {
      clearInterval(iframeTimer)
    }
  }, 1000)

  if (Object.prototype.hasOwnProperty.call(target, '__VUE_DEVTOOLS_GLOBAL_HOOK__')) {
    if (target.__VUE_DEVTOOLS_GLOBAL_HOOK__.devtoolsVersion !== devtoolsVersion) {
      console.error(`Another version of Vue Devtools seems to be installed. Please enable only one version at a time.`)
    }
    return
  }

  let hook

  if (isIframe) {
    const sendToParent = (cb) => {
      try {
        const hook = (window.parent as any).__VUE_DEVTOOLS_GLOBAL_HOOK__
        if (hook) {
          return cb(hook)
        }
        else {
          console.warn('[Vue Devtools] No hook in parent window')
        }
      }
      catch (e) {
        console.warn('[Vue Devtools] Failed to send message to parent window', e)
      }
    }

    hook = {
      devtoolsVersion,
      // eslint-disable-next-line accessor-pairs
      set Vue(value) {
        sendToParent((hook) => {
          hook.Vue = value
        })
      },

      // eslint-disable-next-line accessor-pairs
      set enabled(value) {
        sendToParent((hook) => {
          hook.enabled = value
        })
      },

      on(event, fn) {
        sendToParent(hook => hook.on(event, fn))
      },

      once(event, fn) {
        sendToParent(hook => hook.once(event, fn))
      },

      off(event, fn) {
        sendToParent(hook => hook.off(event, fn))
      },

      emit(event, ...args) {
        sendToParent(hook => hook.emit(event, ...args))
      },

      cleanupBuffer(matchArg) {
        return sendToParent(hook => hook.cleanupBuffer(matchArg)) ?? false
      },
    }
  }
  else {
    hook = {
      devtoolsVersion,
      Vue: null,
      enabled: undefined,
      _buffer: [],
      _bufferMap: new Map(),
      _bufferToRemove: new Map(),
      store: null,
      initialState: null,
      storeModules: null,
      flushStoreModules: null,
      apps: [],

      _replayBuffer(event) {
        const buffer = this._buffer
        this._buffer = []
        this._bufferMap.clear()
        this._bufferToRemove.clear()

        for (let i = 0, l = buffer.length; i < l; i++) {
          const allArgs = buffer[i].slice(1)
          allArgs[0] === event
            // eslint-disable-next-line prefer-spread
            ? this.emit.apply(this, allArgs)
            : this._buffer.push(buffer[i])
        }
      },

      on(event, fn) {
        const $event = `$${event}`
        if (listeners[$event]) {
          listeners[$event].push(fn)
        }
        else {
          listeners[$event] = [fn]
          this._replayBuffer(event)
        }
      },

      once(event, fn) {
        const on = (...args) => {
          this.off(event, on)
          return fn.apply(this, args)
        }
        this.on(event, on)
      },

      off(event, fn) {
        event = `$${event}`
        if (!arguments.length) {
          listeners = {}
        }
        else {
          const cbs = listeners[event]
          if (cbs) {
            if (!fn) {
              listeners[event] = null
            }
            else {
              for (let i = 0, l = cbs.length; i < l; i++) {
                const cb = cbs[i]
                if (cb === fn || cb.fn === fn) {
                  cbs.splice(i, 1)
                  break
                }
              }
            }
          }
        }
      },

      emit(event, ...args) {
        const $event = `$${event}`
        let cbs = listeners[$event]
        if (cbs) {
          cbs = cbs.slice()
          for (let i = 0, l = cbs.length; i < l; i++) {
            try {
              const result = cbs[i].apply(this, args)
              if (typeof result?.catch === 'function') {
                result.catch((e) => {
                  console.error(`[Hook] Error in async event handler for ${event} with args:`, args)
                  console.error(e)
                })
              }
            }
            catch (e) {
              console.error(`[Hook] Error in event handler for ${event} with args:`, args)
              console.error(e)
            }
          }
        }
        else {
          const buffered = [Date.now(), event, ...args]
          this._buffer.push(buffered)

          for (let i = 2; i < args.length; i++) {
            if (typeof args[i] === 'object' && args[i]) {
              // Save by component instance  (3rd, 4th or 5th arg)
              this._bufferMap.set(args[i], buffered)
              break
            }
          }
        }
      },

      /**
       * Remove buffered events with any argument that is equal to the given value.
       * @param matchArg Given value to match.
       */
      cleanupBuffer(matchArg) {
        const inBuffer = this._bufferMap.has(matchArg)
        if (inBuffer) {
          // Mark event for removal
          this._bufferToRemove.set(this._bufferMap.get(matchArg), true)
        }
        return inBuffer
      },

      _cleanupBuffer() {
        const now = Date.now()
        // Clear buffer events that are older than 10 seconds or marked for removal
        this._buffer = this._buffer.filter(args => !this._bufferToRemove.has(args) && now - args[0] < 10_000)
        this._bufferToRemove.clear()
        this._bufferMap.clear()
      },
    }

    setInterval(() => {
      hook._cleanupBuffer()
    }, 10_000)

    hook.once('init', (Vue) => {
      hook.Vue = Vue

      if (Vue) {
        Vue.prototype.$inspect = function () {
          const fn = target.__VUE_DEVTOOLS_INSPECT__
          fn && fn(this)
        }
      }
    })

    hook.on('app:init', (app, version, types) => {
      const appRecord = {
        app,
        version,
        types,
      }
      hook.apps.push(appRecord)
      hook.emit('app:add', appRecord)
    })

    hook.once('vuex:init', (store) => {
      hook.store = store
      hook.initialState = clone(store.state)
      const origReplaceState = store.replaceState.bind(store)
      store.replaceState = (state) => {
        hook.initialState = clone(state)
        origReplaceState(state)
      }
      // Dynamic modules
      let origRegister, origUnregister
      if (store.registerModule) {
        hook.storeModules = []
        origRegister = store.registerModule.bind(store)
        store.registerModule = (path, module, options) => {
          if (typeof path === 'string') {
            path = [path]
          }
          hook.storeModules.push({ path, module, options })
          origRegister(path, module, options)
          if (process.env.NODE_ENV !== 'production') {
            // eslint-disable-next-line no-console
            console.log('early register module', path, module, options)
          }
        }
        origUnregister = store.unregisterModule.bind(store)
        store.unregisterModule = (path) => {
          if (typeof path === 'string') {
            path = [path]
          }
          const key = path.join('/')
          const index = hook.storeModules.findIndex(m => m.path.join('/') === key)
          if (index !== -1) {
            hook.storeModules.splice(index, 1)
          }
          origUnregister(path)
          if (process.env.NODE_ENV !== 'production') {
            // eslint-disable-next-line no-console
            console.log('early unregister module', path)
          }
        }
      }
      hook.flushStoreModules = () => {
        store.replaceState = origReplaceState
        if (store.registerModule) {
          store.registerModule = origRegister
          store.unregisterModule = origUnregister
        }
        return hook.storeModules || []
      }
    })
  }

  Object.defineProperty(target, '__VUE_DEVTOOLS_GLOBAL_HOOK__', {
    get() {
      return hook
    },
  })

  // Handle apps initialized before hook injection
  if (target.__VUE_DEVTOOLS_HOOK_REPLAY__) {
    try {
      target.__VUE_DEVTOOLS_HOOK_REPLAY__.forEach(cb => cb(hook))
      target.__VUE_DEVTOOLS_HOOK_REPLAY__ = []
    }
    catch (e) {
      console.error('[vue-devtools] Error during hook replay', e)
    }
  }

  // Clone deep utility for cloning initial state of the store
  // Forked from https://github.com/planttheidea/fast-copy
  // Last update: 2019-10-30
  // ⚠️ Don't forget to update `./hook.js`

  // utils
  const { toString: toStringFunction } = Function.prototype
  const {
    create,
    defineProperty,
    getOwnPropertyDescriptor,
    getOwnPropertyNames,
    getOwnPropertySymbols,
    getPrototypeOf,
  } = Object
  const { hasOwnProperty, propertyIsEnumerable } = Object.prototype

  /**
   * @enum
   *
   * @const {object} SUPPORTS
   *
   * @property {boolean} SYMBOL_PROPERTIES are symbol properties supported
   * @property {boolean} WEAKSET is WeakSet supported
   */
  const SUPPORTS = {
    SYMBOL_PROPERTIES: typeof getOwnPropertySymbols === 'function',
    WEAKSET: typeof WeakSet === 'function',
  }

  /**
   * @function createCache
   *
   * @description
   * get a new cache object to prevent circular references
   *
   * @returns the new cache object
   */
  const createCache = () => {
    if (SUPPORTS.WEAKSET) {
      return new WeakSet()
    }

    const object = create({
      add: value => object._values.push(value),
      has: value => !!~object._values.indexOf(value),
    })

    object._values = []

    return object
  }

  /**
   * @function getCleanClone
   *
   * @description
   * get an empty version of the object with the same prototype it has
   *
   * @param object the object to build a clean clone from
   * @param realm the realm the object resides in
   * @returns the empty cloned object
   */
  const getCleanClone = (object, realm) => {
    if (!object.constructor) {
      return create(null)
    }

    // eslint-disable-next-line no-proto, no-restricted-properties
    const prototype = object.__proto__ || getPrototypeOf(object)

    if (object.constructor === realm.Object) {
      return prototype === realm.Object.prototype ? {} : create(prototype)
    }

    if (~toStringFunction.call(object.constructor).indexOf('[native code]')) {
      try {
        return new object.constructor()
      }
      catch (e) {
        // Error
      }
    }

    return create(prototype)
  }

  /**
   * @function getObjectCloneLoose
   *
   * @description
   * get a copy of the object based on loose rules, meaning all enumerable keys
   * and symbols are copied, but property descriptors are not considered
   *
   * @param object the object to clone
   * @param realm the realm the object resides in
   * @param handleCopy the function that handles copying the object
   * @returns the copied object
   */
  const getObjectCloneLoose = (
    object,
    realm,
    handleCopy,
    cache,
  ) => {
    const clone = getCleanClone(object, realm)

    for (const key in object) {
      if (hasOwnProperty.call(object, key)) {
        clone[key] = handleCopy(object[key], cache)
      }
    }

    if (SUPPORTS.SYMBOL_PROPERTIES) {
      const symbols = getOwnPropertySymbols(object)

      if (symbols.length) {
        for (let index = 0, symbol; index < symbols.length; index++) {
          symbol = symbols[index]

          if (propertyIsEnumerable.call(object, symbol)) {
            clone[symbol] = handleCopy(object[symbol], cache)
          }
        }
      }
    }

    return clone
  }

  /**
   * @function getObjectCloneStrict
   *
   * @description
   * get a copy of the object based on strict rules, meaning all keys and symbols
   * are copied based on the original property descriptors
   *
   * @param object the object to clone
   * @param realm the realm the object resides in
   * @param handleCopy the function that handles copying the object
   * @returns the copied object
   */
  const getObjectCloneStrict = (
    object,
    realm,
    handleCopy,
    cache,
  ) => {
    const clone = getCleanClone(object, realm)

    const properties = SUPPORTS.SYMBOL_PROPERTIES
      ? [].concat(getOwnPropertyNames(object), getOwnPropertySymbols(object))
      : getOwnPropertyNames(object)

    if (properties.length) {
      for (
        let index = 0, property, descriptor;
        index < properties.length;
        index++
      ) {
        property = properties[index]

        if (property !== 'callee' && property !== 'caller') {
          descriptor = getOwnPropertyDescriptor(object, property)

          descriptor.value = handleCopy(object[property], cache)

          defineProperty(clone, property, descriptor)
        }
      }
    }

    return clone
  }

  /**
   * @function getRegExpFlags
   *
   * @description
   * get the flags to apply to the copied regexp
   *
   * @param regExp the regexp to get the flags of
   * @returns the flags for the regexp
   */
  const getRegExpFlags = (regExp) => {
    let flags = ''

    if (regExp.global) {
      flags += 'g'
    }

    if (regExp.ignoreCase) {
      flags += 'i'
    }

    if (regExp.multiline) {
      flags += 'm'
    }

    if (regExp.unicode) {
      flags += 'u'
    }

    if (regExp.sticky) {
      flags += 'y'
    }

    return flags
  }

  const { isArray } = Array

  const GLOBAL_THIS = (() => {
    // eslint-disable-next-line no-restricted-globals
    if (typeof self !== 'undefined') {
      // eslint-disable-next-line no-restricted-globals
      return self
    }

    if (typeof window !== 'undefined') {
      return window
    }

    if (typeof globalThis !== 'undefined') {
      return globalThis
    }

    if (console && console.error) {
      console.error('Unable to locate global object, returning "this".')
    }
  })()

  /**
   * @function clone
   *
   * @description
   * copy an object deeply as much as possible
   *
   * If `strict` is applied, then all properties (including non-enumerable ones)
   * are copied with their original property descriptors on both objects and arrays.
   *
   * The object is compared to the global constructors in the `realm` provided,
   * and the native constructor is always used to ensure that extensions of native
   * objects (allows in ES2015+) are maintained.
   *
   * @param object the object to copy
   * @param [options] the options for copying with
   * @param [options.isStrict] should the copy be strict
   * @param [options.realm] the realm (this) object the object is copied from
   * @returns the copied object
   */
  function clone(object, options = null) {
    // manually coalesced instead of default parameters for performance
    const isStrict = !!(options && options.isStrict)
    const realm = (options && options.realm) || GLOBAL_THIS

    const getObjectClone = isStrict
      ? getObjectCloneStrict
      : getObjectCloneLoose

    /**
     * @function handleCopy
     *
     * @description
     * copy the object recursively based on its type
     *
     * @param object the object to copy
     * @returns the copied object
     */
    const handleCopy = (
      object,
      cache,
    ) => {
      if (!object || typeof object !== 'object' || cache.has(object)) {
        return object
      }

      // DOM objects
      if (typeof HTMLElement !== 'undefined' && object instanceof HTMLElement) {
        return object.cloneNode(false)
      }

      const Constructor = object.constructor

      // plain objects
      if (Constructor === realm.Object) {
        cache.add(object)

        return getObjectClone(object, realm, handleCopy, cache)
      }

      let clone

      // arrays
      if (isArray(object)) {
        cache.add(object)

        // if strict, include non-standard properties
        if (isStrict) {
          return getObjectCloneStrict(object, realm, handleCopy, cache)
        }

        clone = new Constructor()

        for (let index = 0; index < object.length; index++) {
          clone[index] = handleCopy(object[index], cache)
        }

        return clone
      }

      // dates
      if (object instanceof realm.Date) {
        return new Constructor(object.getTime())
      }

      // regexps
      if (object instanceof realm.RegExp) {
        clone = new Constructor(
          object.source,
          object.flags || getRegExpFlags(object),
        )

        clone.lastIndex = object.lastIndex

        return clone
      }

      // maps
      if (realm.Map && object instanceof realm.Map) {
        cache.add(object)

        clone = new Constructor()

        object.forEach((value, key) => {
          clone.set(key, handleCopy(value, cache))
        })

        return clone
      }

      // sets
      if (realm.Set && object instanceof realm.Set) {
        cache.add(object)

        clone = new Constructor()

        object.forEach((value) => {
          clone.add(handleCopy(value, cache))
        })

        return clone
      }

      // buffers (node-only)
      if (realm.Buffer && realm.Buffer.isBuffer(object)) {
        clone = realm.Buffer.allocUnsafe
          ? realm.Buffer.allocUnsafe(object.length)
          : new Constructor(object.length)

        object.copy(clone)

        return clone
      }

      // arraybuffers / dataviews
      if (realm.ArrayBuffer) {
        // dataviews
        if (realm.ArrayBuffer.isView(object)) {
          return new Constructor(object.buffer.slice(0))
        }

        // arraybuffers
        if (object instanceof realm.ArrayBuffer) {
          return object.slice(0)
        }
      }

      // if the object cannot / should not be cloned, don't
      if (
        // promise-like
        (hasOwnProperty.call(object, 'then') && typeof object.then === 'function')
        // errors
        || object instanceof Error
        // weakmaps
        || (realm.WeakMap && object instanceof realm.WeakMap)
        // weaksets
        || (realm.WeakSet && object instanceof realm.WeakSet)
      ) {
        return object
      }

      cache.add(object)

      // assume anything left is a custom constructor
      return getObjectClone(object, realm, handleCopy, cache)
    }

    return handleCopy(object, createCache())
  }
}


================================================
FILE: packages/app-backend-core/src/index.ts
================================================
import type {
  AppRecord,
  BackendContext,
  Plugin,
} from '@vue-devtools/app-backend-api'
import {
  BuiltinBackendFeature,
  createBackendContext,
} from '@vue-devtools/app-backend-api'
import type {
  Bridge,
} from '@vue-devtools/shared-utils'
import {
  BridgeEvents,
  BridgeSubscriptions,
  BuiltinTabs,
  HookEvents,
  SharedData,
  createThrottleQueue,
  getPluginSettings,
  initSharedData,
  isBrowser,
  parse,
  raf,
  revive,
  target,
} from '@vue-devtools/shared-utils'
import debounce from 'lodash/debounce'
import type { CustomInspectorOptions, PluginDescriptor, SetupFunction, TimelineEventOptions, TimelineLayerOptions } from '@vue/devtools-api'
import { Hooks, now } from '@vue/devtools-api'
import { hook } from './global-hook'
import { isSubscribed, subscribe, unsubscribe } from './util/subscriptions'
import { highlight, unHighlight } from './highlighter'
import { addTimelineEvent, clearTimeline, sendTimelineEventData, sendTimelineLayerEvents, sendTimelineLayers, setupTimeline } from './timeline'
import ComponentPicker from './component-pick'
import {
  editComponentState,
  getComponentId,
  getComponentInstance,
  refreshComponentTreeSearch,
  sendComponentTreeData,
  sendComponentUpdateTracking,
  sendEmptyComponentData,
  sendSelectedComponentData,
} from './component'
import { addPlugin, addPreviouslyRegisteredPlugins, addQueuedPlugins, sendPluginList } from './plugin'
import { _legacy_getAndRegisterApps, getAppRecord, registerApp, removeApp, selectApp, sendApps, waitForAppsRegistration } from './app'
import { editInspectorState, getInspector, getInspectorWithAppId, selectInspectorNode, sendCustomInspectors, sendInspectorState, sendInspectorTree } from './inspector'
import { showScreenshot } from './timeline-screenshot'
import { performanceMarkEnd, performanceMarkStart } from './perf'
import { initOnPageConfig } from './page-config'
import { addTimelineMarker, sendTimelineMarkers } from './timeline-marker'
import { flashComponent } from './flash.js'

let ctx: BackendContext = target.__vdevtools_ctx ?? null
let connected = target.__vdevtools_connected ?? false

let pageTitleObserver: MutationObserver

export async function initBackend(bridge: Bridge) {
  await initSharedData({
    bridge,
    persist: false,
  })

  SharedData.isBrowser = isBrowser

  initOnPageConfig()

  if (!connected) {
    // First connect
    ctx = target.__vdevtools_ctx = createBackendContext({
      bridge,
      hook,
    })

    SharedData.legacyApps = false
    if (hook.Vue) {
      connect()
      _legacy_getAndRegisterApps(ctx, true)
      SharedData.legacyApps = true
    }
    hook.on(HookEvents.INIT, () => {
      _legacy_getAndRegisterApps(ctx, true)
      SharedData.legacyApps = true
    })

    hook.on(HookEvents.APP_ADD, async (app) => {
      await registerApp(app, ctx)
      connect()
    })

    // Add apps that already sent init
    if (hook.apps.length) {
      hook.apps.forEach((app) => {
        registerApp(app, ctx)
        connect()
      })
    }
  }
  else {
    // Reconnect
    ctx.bridge = bridge
    connectBridge()
    ctx.bridge.send(BridgeEvents.TO_FRONT_RECONNECTED)
  }
}

async function connect() {
  if (connected) {
    return
  }
  connected = target.__vdevtools_connected = true

  await waitForAppsRegistration()

  connectBridge()

  ctx.currentTab = BuiltinTabs.COMPONENTS

  // Apps

  hook.on(HookEvents.APP_UNMOUNT, async (app) => {
    await removeApp(app, ctx)
  })

  // Components

  const throttleQueue = createThrottleQueue(500)

  hook.on(HookEvents.COMPONENT_UPDATED, async (app, uid, parentUid, component) => {
    try {
      if (!app || (typeof uid !== 'number' && !uid) || !component) {
        return
      }
      const now = Date.now()

      let id: string
      let appRecord: AppRecord
      if (app && uid != null) {
        id = await getComponentId(app, uid, component, ctx)
        appRecord = await getAppRecord(app, ctx)
      }
      else {
        id = ctx.currentInspectedComponentId
        appRecord = ctx.currentAppRecord
      }

      throttleQueue.add(`update:${id}`, async () => {
        try {
          if (SharedData.trackUpdates) {
            sendComponentUpdateTracking(id, now, ctx)
          }

          if (SharedData.flashUpdates) {
            await flashComponent(component, appRecord.backend)
          }

          // Update component inspector
          if (ctx.currentInspectedComponentId === id) {
            await sendSelectedComponentData(appRecord, ctx.currentInspectedComponentId, ctx)
          }

          // Update tree (tags)
          if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, id)) {
            await sendComponentTreeData(appRecord, id, appRecord.componentFilter, 0, false, ctx)
          }
        }
        catch (e) {
          if (SharedData.debugInfo) {
            console.error(e)
          }
        }
      })
    }
    catch (e) {
      if (SharedData.debugInfo) {
        console.error(e)
      }
    }
  })

  hook.on(HookEvents.COMPONENT_ADDED, async (app, uid, parentUid, component) => {
    try {
      if (!app || (typeof uid !== 'number' && !uid) || !component) {
        return
      }
      const now = Date.now()
      const id = await getComponentId(app, uid, component, ctx)

      throttleQueue.add(`add:${id}`, async () => {
        try {
          const appRecord = await getAppRecord(app, ctx)
          if (component) {
            if (component.__VUE_DEVTOOLS_UID__ == null) {
              component.__VUE_DEVTOOLS_UID__ = id
            }
            if (appRecord?.instanceMap) {
              if (!appRecord.instanceMap.has(id)) {
                appRecord.instanceMap.set(id, component)
              }
            }
          }

          if (parentUid != null && appRecord?.instanceMap) {
            const parentInstances = await appRecord.backend.api.walkComponentParents(component)
            if (parentInstances.length) {
              // Check two parents level to update `hasChildren
              for (let i = 0; i < parentInstances.length; i++) {
                const parentId = await getComponentId(app, parentUid, parentInstances[i], ctx)
                if (i < 2 && isSubscribed(BridgeSubscriptions.COMPONENT_TREE, parentId)) {
                  raf(() => {
                    sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx)
                  })
                }

                if (SharedData.trackUpdates) {
                  sendComponentUpdateTracking(parentId, now, ctx)
                }
              }
            }
          }

          if (ctx.currentInspectedComponentId === id) {
            await sendSelectedComponentData(appRecord, id, ctx)
          }

          if (SharedData.trackUpdates) {
            sendComponentUpdateTracking(id, now, ctx)
          }

          if (SharedData.flashUpdates) {
            await flashComponent(component, appRecord.backend)
          }

          await refreshComponentTreeSearch(ctx)
        }
        catch (e) {
          if (SharedData.debugInfo) {
            console.error(e)
          }
        }
      })
    }
    catch (e) {
      if (SharedData.debugInfo) {
        console.error(e)
      }
    }
  })

  hook.on(HookEvents.COMPONENT_REMOVED, async (app, uid, parentUid, component) => {
    try {
      if (!app || (typeof uid !== 'number' && !uid) || !component) {
        return
      }
      const id = await getComponentId(app, uid, component, ctx)

      throttleQueue.add(`remove:${id}`, async () => {
        try {
          const appRecord = await getAppRecord(app, ctx)
          if (parentUid != null && appRecord) {
            const parentInstances = await appRecord.backend.api.walkComponentParents(component)
            if (parentInstances.length) {
              const parentId = await getComponentId(app, parentUid, parentInstances[0], ctx)
              if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, parentId)) {
                raf(async () => {
                  try {
                    const appRecord = await getAppRecord(app, ctx)

                    if (appRecord) {
                      sendComponentTreeData(appRecord, parentId, appRecord.componentFilter, null, false, ctx)
                    }
                  }
                  catch (e) {
                    if (SharedData.debugInfo) {
                      console.error(e)
                    }
                  }
                })
              }
            }
          }

          if (isSubscribed(BridgeSubscriptions.SELECTED_COMPONENT_DATA, id)) {
            await sendEmptyComponentData(id, ctx)
          }

          if (appRecord) {
            appRecord.instanceMap.delete(id)
          }

          await refreshComponentTreeSearch(ctx)
        }
        catch (e) {
          if (SharedData.debugInfo) {
            console.error(e)
          }
        }
      })
    }
    catch (e) {
      if (SharedData.debugInfo) {
        console.error(e)
      }
    }
  })

  hook.on(HookEvents.TRACK_UPDATE, (id, ctx) => {
    sendComponentUpdateTracking(id, Date.now(), ctx)
  })

  hook.on(HookEvents.FLASH_UPDATE, (instance, backend) => {
    flashComponent(instance, backend)
  })

  // Component perf

  hook.on(HookEvents.PERFORMANCE_START, (app, uid, vm, type, time) => {
    performanceMarkStart(app, uid, vm, type, time, ctx)
  })

  hook.on(HookEvents.PERFORMANCE_END, (app, uid, vm, type, time) => {
    performanceMarkEnd(app, uid, vm, type, time, ctx)
  })

  // Highlighter

  hook.on(HookEvents.COMPONENT_HIGHLIGHT, (instanceId) => {
    highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx)
  })

  hook.on(HookEvents.COMPONENT_UNHIGHLIGHT, () => {
    unHighlight()
  })

  // Timeline

  setupTimeline(ctx)

  hook.on(HookEvents.TIMELINE_LAYER_ADDED, async (options: TimelineLayerOptions, plugin: Plugin) => {
    const appRecord = await getAppRecord(plugin.descriptor.app, ctx)
    if (appRecord) {
      ctx.timelineLayers.push({
        ...options,
        appRecord,
        plugin,
        events: [],
      })
      ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_LAYER_ADD, {})
    }
  })

  hook.on(HookEvents.TIMELINE_EVENT_ADDED, async (options: TimelineEventOptions, plugin: Plugin) => {
    await addTimelineEvent(options, plugin.descriptor.app, ctx)
  })

  // Custom inspectors

  hook.on(HookEvents.CUSTOM_INSPECTOR_ADD, async (options: CustomInspectorOptions, plugin: Plugin) => {
    const appRecord = await getAppRecord(plugin.descriptor.app, ctx)
    if (appRecord) {
      ctx.customInspectors.push({
        ...options,
        appRecord,
        plugin,
        treeFilter: '',
        selectedNodeId: null,
      })
      ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_ADD, {})
    }
  })

  hook.on(HookEvents.CUSTOM_INSPECTOR_SEND_TREE, async (inspectorId: string, plugin: Plugin) => {
    const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx)
    if (inspector) {
      await sendInspectorTree(inspector, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  hook.on(HookEvents.CUSTOM_INSPECTOR_SEND_STATE, async (inspectorId: string, plugin: Plugin) => {
    const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx)
    if (inspector) {
      await sendInspectorState(inspector, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  hook.on(HookEvents.CUSTOM_INSPECTOR_SELECT_NODE, async (inspectorId: string, nodeId: string, plugin: Plugin) => {
    const inspector = getInspector(inspectorId, plugin.descriptor.app, ctx)
    if (inspector) {
      await selectInspectorNode(inspector, nodeId, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  // Plugins

  try {
    await addPreviouslyRegisteredPlugins(ctx)
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(`Error adding previously registered plugins:`)
      console.error(e)
    }
  }
  try {
    await addQueuedPlugins(ctx)
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(`Error adding queued plugins:`)
      console.error(e)
    }
  }

  hook.on(HookEvents.SETUP_DEVTOOLS_PLUGIN, async (pluginDescriptor: PluginDescriptor, setupFn: SetupFunction) => {
    await addPlugin({ pluginDescriptor, setupFn }, ctx)
  })

  target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ = true

  // Legacy flush

  const handleFlush = debounce(async () => {
    if (ctx.currentAppRecord?.backend.options.features.includes(BuiltinBackendFeature.FLUSH)) {
      await sendComponentTreeData(ctx.currentAppRecord, '_root', ctx.currentAppRecord.componentFilter, null, false, ctx)
      if (ctx.currentInspectedComponentId) {
        await sendSelectedComponentData(ctx.currentAppRecord, ctx.currentInspectedComponentId, ctx)
      }
    }
  }, 500)

  hook.off(HookEvents.FLUSH)
  hook.on(HookEvents.FLUSH, handleFlush)

  // Connect done

  try {
    await addTimelineMarker({
      id: 'vue-devtools-init-backend',
      time: now(),
      label: 'Vue Devtools connected',
      color: 0x41B883,
      all: true,
    }, ctx)
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(`Error while adding devtools connected timeline marker:`)
      console.error(e)
    }
  }
}

function connectBridge() {
  // Subscriptions

  ctx.bridge.on(BridgeEvents.TO_BACK_SUBSCRIBE, ({ type, key }) => {
    subscribe(type, key)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_UNSUBSCRIBE, ({ type, key }) => {
    unsubscribe(type, key)
  })

  // Tabs

  ctx.bridge.on(BridgeEvents.TO_BACK_TAB_SWITCH, async (tab) => {
    ctx.currentTab = tab
    await unHighlight()
  })

  // Apps

  ctx.bridge.on(BridgeEvents.TO_BACK_APP_LIST, async () => {
    await sendApps(ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_APP_SELECT, async (id) => {
    if (id == null) {
      return
    }
    const record = ctx.appRecords.find(r => r.id === id)
    if (record) {
      await selectApp(record, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`App with id ${id} not found`)
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_SCAN_LEGACY_APPS, () => {
    if (hook.Vue) {
      _legacy_getAndRegisterApps(ctx)
    }
  })

  // Components

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_TREE, async ({ instanceId, filter, recursively }) => {
    ctx.currentAppRecord.componentFilter = filter
    subscribe(BridgeSubscriptions.COMPONENT_TREE, instanceId)
    await sendComponentTreeData(ctx.currentAppRecord, instanceId, filter, null, recursively, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_SELECTED_DATA, async (instanceId) => {
    await sendSelectedComponentData(ctx.currentAppRecord, instanceId, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_EDIT_STATE, async ({ instanceId, dotPath, type, value, newKey, remove }) => {
    await editComponentState(instanceId, dotPath, type, { value, newKey, remove }, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_INSPECT_DOM, async ({ instanceId }) => {
    const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx)
    if (instance) {
      const [el] = await ctx.currentAppRecord.backend.api.getComponentRootElements(instance)
      if (el) {
        target.__VUE_DEVTOOLS_INSPECT_TARGET__ = el
        ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_INSPECT_DOM, null)
      }
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_SCROLL_TO, async ({ instanceId }) => {
    if (!isBrowser) {
      return
    }
    const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx)
    if (instance) {
      const [el] = await ctx.currentAppRecord.backend.api.getComponentRootElements(instance)
      if (el) {
        if (typeof el.scrollIntoView === 'function') {
          el.scrollIntoView({
            behavior: 'smooth',
            block: 'center',
            inline: 'center',
          })
        }
        else {
          // Handle nodes that don't implement scrollIntoView
          const bounds = await ctx.currentAppRecord.backend.api.getComponentBounds(instance)
          const scrollTarget = document.createElement('div')
          scrollTarget.style.position = 'absolute'
          scrollTarget.style.width = `${bounds.width}px`
          scrollTarget.style.height = `${bounds.height}px`
          scrollTarget.style.top = `${bounds.top}px`
          scrollTarget.style.left = `${bounds.left}px`
          document.body.appendChild(scrollTarget)
          scrollTarget.scrollIntoView({
            behavior: 'smooth',
            block: 'center',
            inline: 'center',
          })
          setTimeout(() => {
            document.body.removeChild(scrollTarget)
          }, 2000)
        }
        highlight(instance, ctx.currentAppRecord.backend, ctx)
        setTimeout(() => {
          unHighlight()
        }, 2000)
      }
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_RENDER_CODE, async ({ instanceId }) => {
    if (!isBrowser) {
      return
    }
    const instance = getComponentInstance(ctx.currentAppRecord, instanceId, ctx)
    if (instance) {
      const { code } = await ctx.currentAppRecord.backend.api.getComponentRenderCode(instance)
      ctx.bridge.send(BridgeEvents.TO_FRONT_COMPONENT_RENDER_CODE, {
        instanceId,
        code,
      })
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_STATE_ACTION, async ({ value, actionIndex }) => {
    const rawAction = value._custom.actions[actionIndex]
    const action = revive(rawAction?.action)
    if (action) {
      try {
        await action()
      }
      catch (e) {
        if (SharedData.debugInfo) {
          console.error(e)
        }
      }
    }
    else if (SharedData.debugInfo) {
      console.warn(`Couldn't revive action ${actionIndex} from`, value)
    }
  })

  // Highlighter

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_MOUSE_OVER, async (instanceId) => {
    await highlight(ctx.currentAppRecord.instanceMap.get(instanceId), ctx.currentAppRecord.backend, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_MOUSE_OUT, async () => {
    await unHighlight()
  })

  // Component picker

  const componentPicker = new ComponentPicker(ctx)

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_PICK, () => {
    componentPicker.startSelecting()
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_COMPONENT_PICK_CANCELED, () => {
    componentPicker.stopSelecting()
  })

  // Timeline

  ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_LAYER_LIST, async () => {
    await sendTimelineLayers(ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_SHOW_SCREENSHOT, async ({ screenshot }) => {
    await showScreenshot(screenshot, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_CLEAR, async () => {
    await clearTimeline(ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_EVENT_DATA, async ({ id }) => {
    await sendTimelineEventData(id, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_LAYER_LOAD_EVENTS, async ({ appId, layerId }) => {
    await sendTimelineLayerEvents(appId, layerId, ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_TIMELINE_LOAD_MARKERS, async () => {
    await sendTimelineMarkers(ctx)
  })

  // Custom inspectors

  ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_LIST, async () => {
    await sendCustomInspectors(ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_TREE, async ({ inspectorId, appId, treeFilter }) => {
    const inspector = await getInspectorWithAppId(inspectorId, appId, ctx)
    if (inspector) {
      inspector.treeFilter = treeFilter
      sendInspectorTree(inspector, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_STATE, async ({ inspectorId, appId, nodeId }) => {
    const inspector = await getInspectorWithAppId(inspectorId, appId, ctx)
    if (inspector) {
      inspector.selectedNodeId = nodeId
      sendInspectorState(inspector, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_EDIT_STATE, async ({ inspectorId, appId, nodeId, path, type, payload }) => {
    const inspector = await getInspectorWithAppId(inspectorId, appId, ctx)
    if (inspector) {
      await editInspectorState(inspector, nodeId, path, type, payload, ctx)
      inspector.selectedNodeId = nodeId
      await sendInspectorState(inspector, ctx)
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_CUSTOM_INSPECTOR_ACTION, async ({ inspectorId, appId, actionIndex, actionType, args }) => {
    const inspector = await getInspectorWithAppId(inspectorId, appId, ctx)
    if (inspector) {
      const action = inspector[actionType ?? 'actions'][actionIndex]
      try {
        await action.action(...(args ?? []))
      }
      catch (e) {
        if (SharedData.debugInfo) {
          console.error(e)
        }
      }
    }
    else if (SharedData.debugInfo) {
      console.warn(`Inspector ${inspectorId} not found`)
    }
  })

  // Misc

  ctx.bridge.on(BridgeEvents.TO_BACK_LOG, (payload: { level: string, value: any, serialized?: boolean, revive?: boolean }) => {
    let value = payload.value
    if (payload.serialized) {
      value = parse(value, payload.revive)
    }
    else if (payload.revive) {
      value = revive(value)
    }
    // eslint-disable-next-line no-console
    console[payload.level](value)
  })

  // Plugins

  ctx.bridge.on(BridgeEvents.TO_BACK_DEVTOOLS_PLUGIN_LIST, async () => {
    await sendPluginList(ctx)
  })

  ctx.bridge.on(BridgeEvents.TO_BACK_DEVTOOLS_PLUGIN_SETTING_UPDATED, ({ pluginId, key, newValue, oldValue }) => {
    const settings = getPluginSettings(pluginId)
    ctx.hook.emit(HookEvents.PLUGIN_SETTINGS_SET, pluginId, settings)
    ctx.currentAppRecord.backend.api.callHook(Hooks.SET_PLUGIN_SETTINGS, {
      app: ctx.currentAppRecord.options.app,
      pluginId,
      key,
      newValue,
      oldValue,
      settings,
    })
  })

  ctx.bridge.send(BridgeEvents.TO_FRONT_TITLE, { title: document.title })
  // Watch page title
  const titleEl = document.querySelector('title')
  if (titleEl && typeof MutationObserver !== 'undefined') {
    if (pageTitleObserver) {
      pageTitleObserver.disconnect()
    }
    pageTitleObserver = new MutationObserver((mutations) => {
      const title = mutations[0].target as HTMLTitleElement
      ctx.bridge.send(BridgeEvents.TO_FRONT_TITLE, { title: title.textContent })
    })
    pageTitleObserver.observe(titleEl, { subtree: true, characterData: true, childList: true })
  }
}


================================================
FILE: packages/app-backend-core/src/inspector.ts
================================================
import type { App } from '@vue/devtools-api'
import type { BackendContext, CustomInspector } from '@vue-devtools/app-backend-api'
import { BridgeEvents, parse, stringify } from '@vue-devtools/shared-utils'

export function getInspector(inspectorId: string, app: App, ctx: BackendContext) {
  return ctx.customInspectors.find(i => i.id === inspectorId && i.appRecord.options.app === app)
}

export async function getInspectorWithAppId(inspectorId: string, appId: string, ctx: BackendContext): Promise<CustomInspector> {
  for (const i of ctx.customInspectors) {
    if (i.id === inspectorId && i.appRecord.id === appId) {
      return i
    }
  }
  return null
}

export async function sendInspectorTree(inspector: CustomInspector, ctx: BackendContext) {
  const rootNodes = await inspector.appRecord.backend.api.getInspectorTree(inspector.id, inspector.appRecord.options.app, inspector.treeFilter)
  ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_TREE, {
    appId: inspector.appRecord.id,
    inspectorId: inspector.id,
    rootNodes,
  })
}

export async function sendInspectorState(inspector: CustomInspector, ctx: BackendContext) {
  const state = inspector.selectedNodeId ? await inspector.appRecord.backend.api.getInspectorState(inspector.id, inspector.appRecord.options.app, inspector.selectedNodeId) : null
  ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_STATE, {
    appId: inspector.appRecord.id,
    inspectorId: inspector.id,
    state: stringify(state),
  })
}

export async function editInspectorState(inspector: CustomInspector, nodeId: string, dotPath: string, type: string, state: any, _ctx: BackendContext) {
  await inspector.appRecord.backend.api.editInspectorState(inspector.id, inspector.appRecord.options.app, nodeId, dotPath, type, {
    ...state,
    value: state.value != null ? parse(state.value, true) : state.value,
  })
}

export async function sendCustomInspectors(ctx: BackendContext) {
  const inspectors = []
  for (const i of ctx.customInspectors) {
    inspectors.push({
      id: i.id,
      appId: i.appRecord.id,
      pluginId: i.plugin.descriptor.id,
      label: i.label,
      icon: i.icon,
      treeFilterPlaceholder: i.treeFilterPlaceholder,
      stateFilterPlaceholder: i.stateFilterPlaceholder,
      noSelectionText: i.noSelectionText,
      actions: i.actions?.map(a => ({
        icon: a.icon,
        tooltip: a.tooltip,
      })),
      nodeActions: i.nodeActions?.map(a => ({
        icon: a.icon,
        tooltip: a.tooltip,
      })),
    })
  }
  ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_LIST, {
    inspectors,
  })
}

export async function selectInspectorNode(inspector: CustomInspector, nodeId: string, ctx: BackendContext) {
  ctx.bridge.send(BridgeEvents.TO_FRONT_CUSTOM_INSPECTOR_SELECT_NODE, {
    appId: inspector.appRecord.id,
    inspectorId: inspector.id,
    nodeId,
  })
}


================================================
FILE: packages/app-backend-core/src/legacy/scan.ts
================================================
import { isBrowser, target } from '@vue-devtools/shared-utils'
import { getPageConfig } from '../page-config'

const rootInstances = []

/**
 * Scan the page for root level Vue instances.
 */
export function scan() {
  rootInstances.length = 0

  let inFragment = false
  let currentFragment = null

  function processInstance(instance) {
    if (instance) {
      if (!rootInstances.includes(instance.$root)) {
        instance = instance.$root
      }
      if (instance._isFragment) {
        inFragment = true
        currentFragment = instance
      }

      // respect Vue.config.devtools option
      let baseVue = instance.constructor
      while (baseVue.super) {
        baseVue = baseVue.super
      }
      if (baseVue.config && baseVue.config.devtools) {
        rootInstances.push(instance)
      }

      return true
    }
  }

  if (isBrowser) {
    const walkDocument = (document) => {
      walk(document, (node) => {
        if (inFragment) {
          if (node === currentFragment._fragmentEnd) {
            inFragment = false
            currentFragment = null
          }
          return true
        }
        const instance = node.__vue__

        return processInstance(instance)
      })
    }
    walkDocument(document)

    const iframes = document.querySelectorAll<HTMLIFrameElement>('iframe')
    for (const iframe of iframes) {
      try {
        walkDocument(iframe.contentDocument)
      }
      catch (e) {
        // Ignore
      }
    }

    // Scan for Vue instances in the customTarget elements
    const { customVue2ScanSelector } = getPageConfig()
    const customTargets = customVue2ScanSelector ? document.querySelectorAll(customVue2ScanSelector) : []
    for (const customTarget of customTargets) {
      try {
        walkDocument(customTarget)
      }
      catch (e) {
        // Ignore
      }
    }
  }
  else {
    if (Array.isArray(target.__VUE_ROOT_INSTANCES__)) {
      target.__VUE_ROOT_INSTANCES__.map(processInstance)
    }
  }

  return rootInstances
}

/**
 * DOM walk helper
 *
 * @param {NodeList} nodes
 * @param {Function} fn
 */

function walk(node, fn) {
  if (node.childNodes) {
    for (let i = 0, l = node.childNodes.length; i < l; i++) {
      const child = node.childNodes[i]
      const stop = fn(child)
      if (!stop) {
        walk(child, fn)
      }
    }
  }

  // also walk shadow DOM
  if (node.shadowRoot) {
    walk(node.shadowRoot, fn)
  }
}


================================================
FILE: packages/app-backend-core/src/page-config.ts
================================================
import { SharedData, target } from '@vue-devtools/shared-utils'

export interface PageConfig {
  openInEditorHost?: string
  defaultSelectedAppId?: string
  customVue2ScanSelector?: string
}

let config: PageConfig = {}

export function getPageConfig(): PageConfig {
  return config
}

export function initOnPageConfig() {
  // User project devtools config
  if (Object.hasOwnProperty.call(target, 'VUE_DEVTOOLS_CONFIG')) {
    config = SharedData.pageConfig = target.VUE_DEVTOOLS_CONFIG

    // Open in editor
    if (Object.hasOwnProperty.call(config, 'openInEditorHost')) {
      SharedData.openInEditorHost = config.openInEditorHost
    }
  }
}


================================================
FILE: packages/app-backend-core/src/perf.ts
================================================
import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api'
import type { App, ComponentInstance } from '@vue/devtools-api'
import { BridgeSubscriptions, SharedData, raf } from '@vue-devtools/shared-utils'
import { addTimelineEvent } from './timeline'
import { getAppRecord } from './app'
import { getComponentId, sendComponentTreeData } from './component'
import { isSubscribed } from './util/subscriptions'

const markEndQueue = new Map<string, {
  app: App
  uid: number
  instance: ComponentInstance
  type: string
  time: number
}>()

export async function performanceMarkStart(
  app: App,
  uid: number,
  instance: ComponentInstance,
  type: string,
  time: number,
  ctx: BackendContext,
) {
  try {
    if (!SharedData.performanceMonitoringEnabled) {
      return
    }
    const appRecord = await getAppRecord(app, ctx)
    if (!appRecord) {
      return
    }
    const componentName = await appRecord.backend.api.getComponentName(instance)
    const groupId = ctx.perfUniqueGroupId++
    const groupKey = `${uid}-${type}`
    appRecord.perfGroupIds.set(groupKey, { groupId, time })
    await addTimelineEvent({
      layerId: 'performance',
      event: {
        time,
        data: {
          component: componentName,
          type,
          measure: 'start',
        },
        title: componentName,
        subtitle: type,
        groupId,
      },
    }, app, ctx)

    if (markEndQueue.has(groupKey)) {
      const {
        app,
        uid,
        instance,
        type,
        time,
      } = markEndQueue.get(groupKey)
      markEndQueue.delete(groupKey)
      await performanceMarkEnd(
        app,
        uid,
        instance,
        type,
        time,
        ctx,
      )
    }
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(e)
    }
  }
}

export async function performanceMarkEnd(
  app: App,
  uid: number,
  instance: ComponentInstance,
  type: string,
  time: number,
  ctx: BackendContext,
) {
  try {
    if (!SharedData.performanceMonitoringEnabled) {
      return
    }
    const appRecord = await getAppRecord(app, ctx)
    if (!appRecord) {
      return
    }
    const componentName = await appRecord.backend.api.getComponentName(instance)
    const groupKey = `${uid}-${type}`
    const groupInfo = appRecord.perfGroupIds.get(groupKey)
    if (!groupInfo) {
      markEndQueue.set(groupKey, {
        app,
        uid,
        instance,
        type,
        time,
      })
      return
    }
    const { groupId, time: startTime } = groupInfo
    const duration = time - startTime
    await addTimelineEvent({
      layerId: 'performance',
      event: {
        time,
        data: {
          component: componentName,
          type,
          measure: 'end',
          duration: {
            _custom: {
              type: 'Duration',
              value: duration,
              display: `${duration} ms`,
            },
          },
        },
        title: componentName,
        subtitle: type,
        groupId,
      },
    }, app, ctx)

    // Mark on component
    const tooSlow = duration > 10
    if (tooSlow || instance.__VUE_DEVTOOLS_SLOW__) {
      let change = false
      if (tooSlow && !instance.__VUE_DEVTOOLS_SLOW__) {
        instance.__VUE_DEVTOOLS_SLOW__ = {
          duration: null,
          measures: {},
        }
      }

      const data = instance.__VUE_DEVTOOLS_SLOW__

      if (tooSlow && (data.duration == null || data.duration < duration)) {
        data.duration = duration
        change = true
      }

      if (data.measures[type] == null || data.measures[type] < duration) {
        data.measures[type] = duration
        change = true
      }

      if (change) {
        // Update component tree
        const id = await getComponentId(app, uid, instance, ctx)
        if (isSubscribed(BridgeSubscriptions.COMPONENT_TREE, id)) {
          raf(() => {
            sendComponentTreeData(appRecord, id, ctx.currentAppRecord.componentFilter, null, false, ctx)
          })
        }
      }
    }
  }
  catch (e) {
    if (SharedData.debugInfo) {
      console.error(e)
    }
  }
}

export function handleAddPerformanceTag(backend: DevtoolsBackend, _ctx: BackendContext) {
  backend.api.on.visitComponentTree((payload) => {
    if (payload.componentInstance.__VUE_DEVTOOLS_SLOW__) {
      const { duration, measures } = payload.componentInstance.__VUE_DEVTOOLS_SLOW__

      let tooltip = '<div class="grid grid-cols-2 gap-2 font-mono text-xs">'
      for (const type in measures) {
        const d = measures[type]
        tooltip += `<div>${type}</div><div class="text-right text-black rounded px-1 ${d > 30 ? 'bg-red-400' : d > 10 ? 'bg-yellow-400' : 'bg-green-400'}">${Math.round(d * 1000) / 1000} ms</div>`
      }
      tooltip += '</div>'

      payload.treeNode.tags.push({
        backgroundColor: duration > 30 ? 0xF87171 : 0xFBBF24,
        textColor: 0x000000,
        label: `${Math.round(duration * 1000) / 1000} ms`,
        tooltip,
      })
    }
  })
}


================================================
FILE: packages/app-backend-core/src/plugin.ts
================================================
import type { PluginQueueItem } from '@vue/devtools-api'
import type { BackendContext, Plugin } from '@vue-devtools/app-backend-api'
import { DevtoolsPluginApiInstance } from '@vue-devtools/app-backend-api'
import { BridgeEvents, SharedData, target } from '@vue-devtools/shared-utils'
import { getAppRecord, getAppRecordId } from './app'

export async function addPlugin(pluginQueueItem: PluginQueueItem, ctx: BackendContext) {
  const { pluginDescriptor, setupFn } = pluginQueueItem

  const plugin: Plugin = {
    descriptor: pluginDescriptor,
    setupFn,
    error: null,
  }
  ctx.currentPlugin = plugin
  try {
    const appRecord = await getAppRecord(plugin.descriptor.app, ctx)
    if (!appRecord) {
      return
    }
    const api = new DevtoolsPluginApiInstance(plugin, appRecord, ctx)
    if (pluginQueueItem.proxy) {
      await pluginQueueItem.proxy.setRealTarget(api)
    }
    else {
      setupFn(api)
    }
  }
  catch (e) {
    plugin.error = e
    if (SharedData.debugInfo) {
      console.error(e)
    }
  }
  ctx.currentPlugin = null
  ctx.plugins.push(plugin)
  ctx.bridge.send(BridgeEvents.TO_FRONT_DEVTOOLS_PLUGIN_ADD, {
    plugin: await serializePlugin(plugin),
  })

  const targetList = target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__ = target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__ || []
  targetList.push({
    pluginDescriptor,
    setupFn,
  })
}

export async function addQueuedPlugins(ctx: BackendContext) {
  if (target.__VUE_DEVTOOLS_PLUGINS__ && Array.isArray(target.__VUE_DEVTOOLS_PLUGINS__)) {
    for (const queueItem of target.__VUE_DEVTOOLS_PLUGINS__) {
      await addPlugin(queueItem, ctx)
    }
    target.__VUE_DEVTOOLS_PLUGINS__ = null
  }
}

export async function addPreviouslyRegisteredPlugins(ctx: BackendContext) {
  if (target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__ && Array.isArray(target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__)) {
    for (const queueItem of target.__VUE_DEVTOOLS_REGISTERED_PLUGINS__) {
      await addPlugin(queueItem, ctx)
    }
  }
}

export async function sendPluginList(ctx: BackendContext) {
  ctx.bridge.send(BridgeEvents.TO_FRONT_DEVTOOLS_PLUGIN_LIST, {
    plugins: await Promise.all(ctx.plugins.map(p => serializePlugin(p))),
  })
}

export async function serializePlugin(plugin: Plugin) {
  return {
    id: plugin.descriptor.id,
    label: plugin.descriptor.label,
    appId: getAppRecordId(plugin.descriptor.app),
    packageName: plugin.descriptor.packageName,
    homepage: plugin.descriptor.homepage,
    logo: plugin.descriptor.logo,
    componentStateTypes: plugin.descriptor.componentStateTypes,
    settingsSchema: plugin.descriptor.settings,
  }
}


================================================
FILE: packages/app-backend-core/src/timeline-builtins.ts
================================================
import type { TimelineLayerOptions } from '@vue/devtools-api'

export const builtinLayers: TimelineLayerOptions[] = [
  {
    id: 'mouse',
    label: 'Mouse',
    color: 0xA451AF,
    screenshotOverlayRender(event, { events }) {
      const samePositionEvent = events.find(e => e !== event && e.renderMeta.textEl && e.data.x === event.data.x && e.data.y === event.data.y)
      if (samePositionEvent) {
        const text = document.createElement('div')
        text.textContent = event.data.type
        samePositionEvent.renderMeta.textEl.appendChild(text)
        return false
      }

      const div = document.createElement('div')
      div.style.position = 'absolute'
      div.style.left = `${event.data.x - 4}px`
      div.style.top = `${event.data.y - 4}px`
      div.style.width = '8px'
      div.style.height = '8px'
      div.style.borderRadius = '100%'
      div.style.backgroundColor = 'rgba(164, 81, 175, 0.5)'

      const text = document.createElement('div')
      text.textContent = event.data.type
      text.style.color = '#541e5b'
      text.style.fontFamily = 'monospace'
      text.style.fontSize = '9px'
      text.style.position = 'absolute'
      text.style.left = '10px'
      text.style.top = '10px'
      text.style.padding = '1px'
      text.style.backgroundColor = 'rgba(255, 255, 255, 0.9)'
      text.style.borderRadius = '3px'
      div.appendChild(text)

      event.renderMeta.textEl = text

      return div
    },
  },
  {
    id: 'keyboard',
    label: 'Keyboard',
    color: 0x8151AF,
  },
  {
    id: 'component-event',
    label: 'Component events',
    color: 0x41B883,
    screenshotOverlayRender: (event, { events }) => {
      if (!event.meta.bounds || events.some(e => e !== event && e.layerId === event.layerId && e.renderMeta.drawn && (e.meta.componentId === event.meta.componentId || (
        e.meta.bounds.left === event.meta.bounds.left
        && e.meta.bounds.top === event.meta.bounds.top
        && e.meta.bounds.width === event.meta.bounds.width
        && e.meta.bounds.height === event.meta.bounds.height
      )))) {
        return false
      }

      const div = document.createElement('div')
      div.style.position = 'absolute'
      div.style.left = `${event.meta.bounds.left - 4}px`
      div.style.top = `${event.meta.bounds.top - 4}px`
      div.style.width = `${event.meta.bounds.width}px`
      div.style.height = `${event.meta.bounds.height}px`
      div.style.borderRadius = '8px'
      div.style.borderStyle = 'solid'
      div.style.borderWidth = '4px'
      div.style.borderColor = 'rgba(65, 184, 131, 0.5)'
      div.style.textAlign = 'center'
      div.style.display = 'flex'
      div.style.alignItems = 'center'
      div.style.justifyContent = 'center'
      div.style.overflow = 'hidden'

      const text = document.createElement('div')
      text.style.color = '#267753'
      text.style.fontFamily = 'monospace'
      text.style.fontSize = '9px'
      text.style.padding = '1px'
      text.style.backgroundColor = 'rgba(255, 255, 255, 0.9)'
      text.style.borderRadius = '3px'
      text.textContent = event.data.event
      div.appendChild(text)

      event.renderMeta.drawn = true

      return div
    },
  },
  {
    id: 'performance',
    label: 'Performance',
    color: 0x41B86A,
    groupsOnly: true,
    skipScreenshots: true,
    ignoreNoDurationGroups: true,
  },
]


================================================
FILE: packages/app-backend-core/src/timeline-marker.ts
================================================
import type { BackendContext, TimelineMarker } from '@vue-devtools/app-backend-api'
import { BridgeEvents, SharedData } from '@vue-devtools/shared-utils'
import type { TimelineMarkerOptions } from '@vue/devtools-api'
import { isPerformanceSupported } from '@vue/devtools-api'
import { dateThreshold, perfTimeDiff } from './timeline'

export async function addTimelineMarker(options: TimelineMarkerOptions, ctx: BackendContext) {
  if (!SharedData.timelineRecording) {
    return
  }
  if (!ctx.currentAppRecord) {
    options.all = true
  }
  const marker: TimelineMarker = {
    ...options,
    appRecord: options.all ? null : ctx.currentAppRecord,
  }
  ctx.timelineMarkers.push(marker)
  ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_MARKER, {
    marker: await serializeMarker(marker),
    appId: ctx.currentAppRecord?.id,
  })
}

export async function sendTimelineMarkers(ctx: BackendContext) {
  if (!SharedData.timelineRecording) {
    return
  }
  if (!ctx.currentAppRecord) {
    return
  }
  const markers = ctx.timelineMarkers.filter(marker => marker.all || marker.appRecord === ctx.currentAppRecord)
  const result = []
  for (const marker of markers) {
    result.push(await serializeMarker(marker))
  }
  ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_LOAD_MARKERS, {
    markers: result,
    appId: ctx.currentAppRecord.id,
  })
}

async function serializeMarker(marker: TimelineMarker) {
  let time = marker.time
  if (isPerformanceSupported() && time < dateThreshold) {
    time += perfTimeDiff
  }
  return {
    id: marker.id,
    appId: marker.appRecord?.id,
    all: marker.all,
    time: Math.round(time * 1000),
    label: marker.label,
    color: marker.color,
  }
}


================================================
FILE: packages/app-backend-core/src/timeline-screenshot.ts
================================================
import type { BackendContext } from '@vue-devtools/app-backend-api'
import type { ID, ScreenshotOverlayRenderContext } from '@vue/devtools-api'
import { SharedData } from '@vue-devtools/shared-utils'
import { JobQueue } from './util/queue'
import { builtinLayers } from './timeline-builtins'

let overlay: HTMLDivElement
let image: HTMLImageElement
let container: HTMLDivElement

const jobQueue = new JobQueue()

interface Screenshot {
  id: ID
  time: number
  image: string
  events: ID[]
}

export async function showScreenshot(screenshot: Screenshot, ctx: BackendContext) {
  await jobQueue.queue('showScreenshot', async () => {
    if (screenshot) {
      if (!container) {
        createElements()
      }

      image.src = screenshot.image
      image.style.visibility = screenshot.image ? 'visible' : 'hidden'

      clearContent()

      const events = screenshot.events.map(id => ctx.timelineEventMap.get(id)).filter(Boolean).map(eventData => ({
        layer: builtinLayers.concat(ctx.timelineLayers).find(layer => layer.id === eventData.layerId),
        event: {
          ...eventData.event,
          layerId: eventData.layerId,
          renderMeta: {},
        },
      }))

      const renderContext: ScreenshotOverlayRenderContext = {
        screenshot,
        events: events.map(({ event }) => event),
        index: 0,
      }

      for (let i = 0; i < events.length; i++) {
        const { layer, event } = events[i]
        if (layer.screenshotOverlayRender) {
          renderContext.index = i
          try {
            const result = await layer.screenshotOverlayRender(event, renderContext)
            if (result !== false) {
              if (typeof result === 'string') {
                container.innerHTML += result
              }
              else {
                container.appendChild(result)
              }
            }
          }
          catch (e) {
            if (SharedData.debugInfo) {
              console.error(e)
            }
          }
        }
      }

      showElement()
    }
    else {
      hideElement()
    }
  })
}

function createElements() {
  overlay = document.createElement('div')
  overlay.style.position = 'fixed'
  overlay.style.zIndex = '9999999999999'
  overlay.style.pointerEvents = 'none'
  overlay.style.left = '0'
  overlay.style.top = '0'
  overlay.style.width = '100vw'
  overlay.style.height = '100vh'
  overlay.style.backgroundColor = 'rgba(0,0,0,0.5)'
  overlay.style.overflow = 'hidden'

  const imageBox = document.createElement('div')
  imageBox.style.position = 'relative'
  overlay.appendChild(imageBox)

  image = document.createElement('img')
  imageBox.appendChild(image)

  container = document.createElement('div')
  container.style.position = 'absolute'
  container.style.left = '0'
  container.style.top = '0'
  imageBox.appendChild(container)

  const style = document.createElement('style')
  style.innerHTML = '.__vuedevtools_no-scroll { overflow: hidden; }'
  document.head.appendChild(style)
}

function showElement() {
  if (!overlay.parentNode) {
    document.body.appendChild(overlay)
    document.body.classList.add('__vuedevtools_no-scroll')
  }
}

function hideElement() {
  if (overlay && overlay.parentNode) {
    overlay.parentNode.removeChild(overlay)

    document.body.classList.remove('__vuedevtools_no-scroll')

    clearContent()
  }
}

function clearContent() {
  while (container.firstChild) {
    container.removeChild(container.lastChild)
  }
}


================================================
FILE: packages/app-backend-core/src/timeline.ts
================================================
import type { AppRecord, BackendContext } from '@vue-devtools/app-backend-api'
import { BridgeEvents, HookEvents, SharedData, isBrowser, stringify } from '@vue-devtools/shared-utils'
import type { App, ID, TimelineEventOptions, WithId } from '@vue/devtools-api'
import { isPerformanceSupported, now } from '@vue/devtools-api'
import { hook } from './global-hook'
import { getAppRecord, getAppRecordId } from './app'
import { builtinLayers } from './timeline-builtins'

export function setupTimeline(ctx: BackendContext) {
  setupBuiltinLayers(ctx)
}

export function addBuiltinLayers(appRecord: AppRecord, ctx: BackendContext) {
  for (const layerDef of builtinLayers) {
    ctx.timelineLayers.push({
      ...layerDef,
      appRecord,
      plugin: null,
      events: [],
    })
  }
}

function setupBuiltinLayers(ctx: BackendContext) {
  if (isBrowser) {
    (['mousedown', 'mouseup', 'click', 'dblclick'] as const).forEach((eventType) => {
      window.addEventListener(eventType, async (event: MouseEvent) => {
        await addTimelineEvent({
          layerId: 'mouse',
          event: {
            time: now(),
            data: {
              type: eventType,
              x: event.clientX,
              y: event.clientY,
            },
            title: eventType,
          },
        }, null, ctx)
      }, {
        capture: true,
        passive: true,
      })
    })

    ;(['keyup', 'keydown', 'keypress'] as const).forEach((eventType) => {
      window.addEventListener(eventType, async (event: KeyboardEvent) => {
        await addTimelineEvent({
          layerId: 'keyboard',
          event: {
            time: now(),
            data: {
              type: eventType,
              key: event.key,
              ctrlKey: event.ctrlKey,
              shiftKey: event.shiftKey,
              altKey: event.altKey,
              metaKey: event.metaKey,
            },
            title: event.key,
          },
        }, null, ctx)
      }, {
        capture: true,
        passive: true,
      })
    })
  }

  hook.on(HookEvents.COMPONENT_EMIT, async (app, instance, event, params) => {
    try {
      if (!SharedData.componentEventsEnabled) {
        return
      }

      const appRecord = await getAppRecord(app, ctx)
      if (!appRecord) {
        return
      }
      const componentId = `${appRecord.id}:${instance.uid}`
      const componentDisplay = (await appRecord.backend.api.getComponentName(instance)) || '<i>Unknown Component</i>'

      await addTimelineEvent({
        layerId: 'component-event',
        event: {
          time: now(),
          data: {
            component: {
              _custom: {
                type: 'component-definition',
                display: componentDisplay,
              },
            },
            event,
            params,
          },
          title: event,
          subtitle: `by ${componentDisplay}`,
          meta: {
            componentId,
            bounds: await appRecord.backend.api.getComponentBounds(instance),
          },
        },
      }, app, ctx)
    }
    catch (e) {
      if (SharedData.debugInfo) {
        console.error(e)
      }
    }
  })
}

export async function sendTimelineLayers(ctx: BackendContext) {
  const layers = []
  for (const layer of ctx.timelineLayers) {
    try {
      layers.push({
        id: layer.id,
        label: layer.label,
        color: layer.color,
        appId: layer.appRecord?.id,
        pluginId: layer.plugin?.descriptor.id,
        groupsOnly: layer.groupsOnly,
        skipScreenshots: layer.skipScreenshots,
        ignoreNoDurationGroups: layer.ignoreNoDurationGroups,
      })
    }
    catch (e) {
      if (SharedData.debugInfo) {
        console.error(e)
      }
    }
  }
  ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_LAYER_LIST, {
    layers,
  })
}

export async function addTimelineEvent(options: TimelineEventOptions, app: App, ctx: BackendContext) {
  if (!SharedData.timelineRecording) {
    return
  }
  const appId = app ? getAppRecordId(app) : null
  const isAllApps = options.all || !app || appId == null

  const id = ctx.nextTimelineEventId++

  const eventData: TimelineEventOptions & WithId = {
    id,
    ...options,
    all: isAllApps,
  }
  ctx.timelineEventMap.set(eventData.id, eventData)

  ctx.bridge.send(BridgeEvents.TO_FRONT_TIMELINE_EVENT, {
    appId: eventData.all ? 'all' : appId,
    layerId: eventData.layerId,
    event: mapTimeline
Download .txt
gitextract_85f6hf0f/

├── .browserslistrc
├── .circleci/
│   └── config.yml
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── commit-convention.md
│   └── workflows/
│       └── create-release.yml
├── .gitignore
├── .vscode/
│   └── settings.json
├── LICENSE
├── README.md
├── babel.config.js
├── cypress/
│   ├── .eslintrc.js
│   ├── .gitignore
│   ├── fixtures/
│   │   └── example.json
│   ├── integration/
│   │   ├── component-data-edit.js
│   │   ├── components-tab.js
│   │   ├── events-tab.js
│   │   ├── vuex-edit.js
│   │   └── vuex-tab.js
│   ├── plugins/
│   │   └── index.js
│   ├── support/
│   │   ├── commands.js
│   │   └── index.js
│   └── utils/
│       └── suite.js
├── cypress.json
├── eslint.config.js
├── extension-zips.js
├── lerna.json
├── package.json
├── packages/
│   ├── api/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── api/
│   │   │   │   ├── api.ts
│   │   │   │   ├── app.ts
│   │   │   │   ├── component.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── hooks.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── util.ts
│   │   │   ├── const.ts
│   │   │   ├── env.ts
│   │   │   ├── index.ts
│   │   │   ├── plugin.ts
│   │   │   ├── proxy.ts
│   │   │   └── time.ts
│   │   └── tsconfig.json
│   ├── app-backend-api/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── api.ts
│   │   │   ├── app-record.ts
│   │   │   ├── backend-context.ts
│   │   │   ├── backend.ts
│   │   │   ├── global-hook.ts
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   └── plugin.ts
│   │   └── tsconfig.json
│   ├── app-backend-core/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── app.ts
│   │   │   ├── backend.ts
│   │   │   ├── component-pick.ts
│   │   │   ├── component.ts
│   │   │   ├── flash.ts
│   │   │   ├── global-hook.ts
│   │   │   ├── highlighter.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   ├── inspector.ts
│   │   │   ├── legacy/
│   │   │   │   └── scan.ts
│   │   │   ├── page-config.ts
│   │   │   ├── perf.ts
│   │   │   ├── plugin.ts
│   │   │   ├── timeline-builtins.ts
│   │   │   ├── timeline-marker.ts
│   │   │   ├── timeline-screenshot.ts
│   │   │   ├── timeline.ts
│   │   │   ├── toast.ts
│   │   │   └── util/
│   │   │       ├── queue.ts
│   │   │       └── subscriptions.ts
│   │   └── tsconfig.json
│   ├── app-backend-vue1/
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── app-backend-vue2/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   ├── data.ts
│   │   │   │   ├── el.ts
│   │   │   │   ├── perf.ts
│   │   │   │   ├── tree.ts
│   │   │   │   ├── update-tracking.ts
│   │   │   │   └── util.ts
│   │   │   ├── events.ts
│   │   │   ├── index.ts
│   │   │   └── plugin.ts
│   │   └── tsconfig.json
│   ├── app-backend-vue3/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   ├── data.ts
│   │   │   │   ├── el.ts
│   │   │   │   ├── filter.ts
│   │   │   │   ├── tree.ts
│   │   │   │   └── util.ts
│   │   │   ├── index.ts
│   │   │   └── util.ts
│   │   └── tsconfig.json
│   ├── app-frontend/
│   │   ├── package.json
│   │   └── src/
│   │       ├── app.ts
│   │       ├── assets/
│   │       │   ├── github-theme/
│   │       │   │   ├── dark.json
│   │       │   │   └── light.json
│   │       │   └── style/
│   │       │       ├── imports.styl
│   │       │       ├── index.postcss
│   │       │       ├── index.styl
│   │       │       ├── transitions.styl
│   │       │       └── variables.styl
│   │       ├── features/
│   │       │   ├── App.vue
│   │       │   ├── apps/
│   │       │   │   ├── AppSelect.vue
│   │       │   │   ├── AppSelectItem.vue
│   │       │   │   ├── AppSelectPane.vue
│   │       │   │   ├── AppSelectPaneItem.vue
│   │       │   │   ├── index.ts
│   │       │   │   └── vue-version-check.ts
│   │       │   ├── bridge/
│   │       │   │   └── index.ts
│   │       │   ├── chrome/
│   │       │   │   ├── index.ts
│   │       │   │   └── pane-visibility.ts
│   │       │   ├── code/
│   │       │   │   └── CodeEditor.vue
│   │       │   ├── components/
│   │       │   │   ├── ComponentTreeNode.vue
│   │       │   │   ├── ComponentsInspector.vue
│   │       │   │   ├── RenderCode.vue
│   │       │   │   ├── SelectedComponentPane.vue
│   │       │   │   └── composable/
│   │       │   │       ├── components.ts
│   │       │   │       ├── highlight.ts
│   │       │   │       ├── index.ts
│   │       │   │       ├── pick.ts
│   │       │   │       └── setup.ts
│   │       │   ├── connection/
│   │       │   │   ├── AppConnecting.vue
│   │       │   │   ├── AppDisconnected.vue
│   │       │   │   └── index.ts
│   │       │   ├── error/
│   │       │   │   ├── ErrorOverlay.vue
│   │       │   │   └── index.ts
│   │       │   ├── header/
│   │       │   │   ├── AppHeader.vue
│   │       │   │   ├── AppHeaderSelect.vue
│   │       │   │   ├── AppHistoryNav.vue
│   │       │   │   ├── header.ts
│   │       │   │   └── tabs.ts
│   │       │   ├── inspector/
│   │       │   │   ├── DataField.vue
│   │       │   │   ├── StateFields.vue
│   │       │   │   ├── StateInspector.vue
│   │       │   │   ├── StateType.vue
│   │       │   │   └── custom/
│   │       │   │       ├── CustomInspector.vue
│   │       │   │       ├── CustomInspectorNode.vue
│   │       │   │       ├── CustomInspectorSelectedNodePane.vue
│   │       │   │       └── composable.ts
│   │       │   ├── layout/
│   │       │   │   ├── EmptyPane.vue
│   │       │   │   ├── SplitPane.vue
│   │       │   │   └── orientation.ts
│   │       │   ├── plugin/
│   │       │   │   ├── PluginDetails.vue
│   │       │   │   ├── PluginHome.vue
│   │       │   │   ├── PluginListItem.vue
│   │       │   │   ├── PluginPermission.vue
│   │       │   │   ├── PluginSettings.vue
│   │       │   │   ├── PluginSettingsItem.vue
│   │       │   │   ├── PluginSourceDescription.vue
│   │       │   │   ├── PluginSourceIcon.vue
│   │       │   │   ├── Plugins.vue
│   │       │   │   └── index.ts
│   │       │   ├── settings/
│   │       │   │   ├── GlobalSettings.vue
│   │       │   │   └── NewTag.vue
│   │       │   ├── timeline/
│   │       │   │   ├── AskScreenshotPermission.vue
│   │       │   │   ├── LayerItem.vue
│   │       │   │   ├── Timeline.vue
│   │       │   │   ├── TimelineEventInspector.vue
│   │       │   │   ├── TimelineEventList.vue
│   │       │   │   ├── TimelineEventListItem.vue
│   │       │   │   ├── TimelineScrollbar.vue
│   │       │   │   ├── TimelineView.vue
│   │       │   │   └── composable/
│   │       │   │       ├── events.ts
│   │       │   │       ├── index.ts
│   │       │   │       ├── layers.ts
│   │       │   │       ├── markers.ts
│   │       │   │       ├── reset.ts
│   │       │   │       ├── screenshot.ts
│   │       │   │       ├── setup.ts
│   │       │   │       ├── store.ts
│   │       │   │       └── time.ts
│   │       │   ├── ui/
│   │       │   │   ├── components/
│   │       │   │   │   ├── VueButton.vue
│   │       │   │   │   ├── VueDisable.vue
│   │       │   │   │   ├── VueDropdown.vue
│   │       │   │   │   ├── VueDropdownButton.vue
│   │       │   │   │   ├── VueFormField.vue
│   │       │   │   │   ├── VueGroup.vue
│   │       │   │   │   ├── VueGroupButton.vue
│   │       │   │   │   ├── VueIcon.vue
│   │       │   │   │   ├── VueInput.vue
│   │       │   │   │   ├── VueLoadingBar.vue
│   │       │   │   │   ├── VueLoadingIndicator.vue
│   │       │   │   │   ├── VueModal.vue
│   │       │   │   │   ├── VueSelect.vue
│   │       │   │   │   ├── VueSelectButton.vue
│   │       │   │   │   ├── VueSwitch.vue
│   │       │   │   │   └── icons.ts
│   │       │   │   ├── composables/
│   │       │   │   │   ├── useDisableScroll.ts
│   │       │   │   │   └── useDisabled.ts
│   │       │   │   └── index.ts
│   │       │   └── welcome/
│   │       │       └── WelcomeSlideshow.vue
│   │       ├── index.ts
│   │       ├── locales/
│   │       │   └── en.js
│   │       ├── mixins/
│   │       │   ├── data-field-edit.js
│   │       │   ├── entry-list.ts
│   │       │   └── keyboard.ts
│   │       ├── plugins/
│   │       │   ├── global-refs.ts
│   │       │   ├── i18n.ts
│   │       │   ├── index.ts
│   │       │   └── responsive.ts
│   │       ├── router.ts
│   │       ├── shims-global.d.ts
│   │       ├── shims-vue.d.ts
│   │       ├── types/
│   │       │   └── vue.d.ts
│   │       └── util/
│   │           ├── color.ts
│   │           ├── defer.ts
│   │           ├── fonts.ts
│   │           ├── format/
│   │           │   ├── index.ts
│   │           │   ├── time.ts
│   │           │   └── value.ts
│   │           ├── keyboard.ts
│   │           ├── queue.ts
│   │           ├── reactivity.ts
│   │           ├── shared-data.ts
│   │           ├── theme.ts
│   │           └── time.ts
│   ├── build-tools/
│   │   ├── package.json
│   │   └── src/
│   │       ├── createConfig.js
│   │       └── index.js
│   ├── docs/
│   │   ├── package.json
│   │   ├── postcss.config.js
│   │   ├── src/
│   │   │   ├── .vitepress/
│   │   │   │   ├── .gitignore
│   │   │   │   ├── config.js
│   │   │   │   └── theme/
│   │   │   │       ├── custom.css
│   │   │   │       └── index.js
│   │   │   ├── assets/
│   │   │   │   └── vue-devtools-architecture.drawio
│   │   │   ├── components/
│   │   │   │   ├── InstallButton.vue
│   │   │   │   └── InstallButtons.vue
│   │   │   ├── devtools-vue3.md
│   │   │   ├── guide/
│   │   │   │   ├── contributing.md
│   │   │   │   ├── custom-vue2-app-scan-selector.md
│   │   │   │   ├── devtools-perf.md
│   │   │   │   ├── faq.md
│   │   │   │   ├── installation.md
│   │   │   │   └── open-in-editor.md
│   │   │   ├── index.md
│   │   │   ├── plugin/
│   │   │   │   ├── api-reference.md
│   │   │   │   └── plugins-guide.md
│   │   │   └── release.md
│   │   └── tailwind.config.cjs
│   ├── shared-utils/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── backend.ts
│   │   │   ├── bridge.ts
│   │   │   ├── consts.ts
│   │   │   ├── edit.ts
│   │   │   ├── env.ts
│   │   │   ├── index.ts
│   │   │   ├── plugin-permissions.ts
│   │   │   ├── plugin-settings.ts
│   │   │   ├── raf.ts
│   │   │   ├── shared-data.ts
│   │   │   ├── shell.ts
│   │   │   ├── storage.ts
│   │   │   ├── throttle.ts
│   │   │   ├── transfer.ts
│   │   │   └── util.ts
│   │   └── tsconfig.json
│   ├── shell-chrome/
│   │   ├── devtools-background.html
│   │   ├── devtools.html
│   │   ├── manifest.json
│   │   ├── package.json
│   │   ├── popups/
│   │   │   ├── disabled.html
│   │   │   ├── disabled.nuxt.html
│   │   │   ├── enabled.html
│   │   │   ├── enabled.nuxt.html
│   │   │   ├── not-found.html
│   │   │   └── popup.css
│   │   ├── src/
│   │   │   ├── backend.js
│   │   │   ├── detector-exec.js
│   │   │   ├── detector.js
│   │   │   ├── devtools-background.js
│   │   │   ├── devtools.js
│   │   │   ├── hook-exec.js
│   │   │   ├── hook.js
│   │   │   ├── proxy.js
│   │   │   └── service-worker.js
│   │   └── webpack.config.js
│   ├── shell-dev-vue2/
│   │   ├── package.json
│   │   ├── public/
│   │   │   ├── target-electron.html
│   │   │   ├── target-iframe.html
│   │   │   └── target.html
│   │   ├── src/
│   │   │   ├── Child.vue
│   │   │   ├── Counter.vue
│   │   │   ├── EventChild.vue
│   │   │   ├── EventChild1.vue
│   │   │   ├── EventChildCond.vue
│   │   │   ├── Events.vue
│   │   │   ├── Functional.vue
│   │   │   ├── Hidden.vue
│   │   │   ├── Init.vue
│   │   │   ├── MyClass.js
│   │   │   ├── NativeTypes.vue
│   │   │   ├── NoProp.vue
│   │   │   ├── Other.vue
│   │   │   ├── RefTester.vue
│   │   │   ├── Target.vue
│   │   │   ├── TransitionExample.vue
│   │   │   ├── VuexObject.vue
│   │   │   ├── dynamic-module.js
│   │   │   ├── iframe-app.js
│   │   │   ├── index.js
│   │   │   ├── router/
│   │   │   │   ├── ChildRoute.vue
│   │   │   │   ├── NamedRoute.vue
│   │   │   │   ├── ParentRoute.vue
│   │   │   │   ├── RouteOne.vue
│   │   │   │   ├── RouteTwo.vue
│   │   │   │   ├── RouteWithAlias.vue
│   │   │   │   ├── RouteWithBeforeEnter.vue
│   │   │   │   ├── RouteWithParams.vue
│   │   │   │   ├── RouteWithProps.vue
│   │   │   │   ├── RouteWithQuery.vue
│   │   │   │   └── Router.vue
│   │   │   ├── router.js
│   │   │   └── store.js
│   │   └── webpack.config.js
│   ├── shell-dev-vue3/
│   │   ├── package.json
│   │   ├── public/
│   │   │   ├── target-iframe.html
│   │   │   └── target.html
│   │   ├── src/
│   │   │   ├── Animation.vue
│   │   │   ├── App.vue
│   │   │   ├── App3.vue
│   │   │   ├── AsyncComponent.vue
│   │   │   ├── AsyncSetup.vue
│   │   │   ├── Child.vue
│   │   │   ├── Condition.vue
│   │   │   ├── DomOrder.vue
│   │   │   ├── EventEmit.vue
│   │   │   ├── EventNesting.vue
│   │   │   ├── Form.vue
│   │   │   ├── FormSection.vue
│   │   │   ├── Functional.vue
│   │   │   ├── Ghost.vue
│   │   │   ├── Heavy.vue
│   │   │   ├── Hello.vue
│   │   │   ├── IframeApp.vue
│   │   │   ├── IndexComponent/
│   │   │   │   └── index.vue
│   │   │   ├── Mixins.vue
│   │   │   ├── NativeTypes.vue
│   │   │   ├── Nested.vue
│   │   │   ├── NestedMore.vue
│   │   │   ├── Other.vue
│   │   │   ├── Provide.vue
│   │   │   ├── SetupDataLike.vue
│   │   │   ├── SetupRender.js
│   │   │   ├── SetupScript.vue
│   │   │   ├── SetupTSScriptProps.vue
│   │   │   ├── SuspenseExample.vue
│   │   │   ├── VModelExample.vue
│   │   │   ├── devtools-plugin/
│   │   │   │   ├── index.js
│   │   │   │   └── simple.js
│   │   │   ├── iframe-app.js
│   │   │   ├── main.js
│   │   │   ├── router/
│   │   │   │   ├── Page1.vue
│   │   │   │   └── Page2.vue
│   │   │   └── store.js
│   │   └── webpack.config.js
│   ├── shell-electron/
│   │   ├── README.md
│   │   ├── app.html
│   │   ├── app.js
│   │   ├── bin.js
│   │   ├── index.js
│   │   ├── package.json
│   │   ├── server.js
│   │   ├── src/
│   │   │   ├── backend.js
│   │   │   ├── devtools.js
│   │   │   └── hook.js
│   │   ├── types/
│   │   │   └── index.d.ts
│   │   ├── webpack.config.js
│   │   └── webpack.node.config.js
│   ├── shell-firefox/
│   │   ├── .gitignore
│   │   ├── copy.sh
│   │   ├── manifest.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── backend.js
│   │   │   ├── background.js
│   │   │   ├── detector.js
│   │   │   ├── devtools-background.js
│   │   │   ├── devtools.js
│   │   │   ├── hook.js
│   │   │   └── proxy.js
│   │   └── webpack.config.js
│   └── shell-host/
│       ├── package.json
│       ├── public/
│       │   └── index.html
│       ├── src/
│       │   ├── DevIframe.vue
│       │   ├── backend.js
│       │   ├── devtools.js
│       │   └── hook.js
│       └── webpack.config.js
├── postcss.config.js
├── release.js
├── sign-firefox.js
├── tailwind.config.js
├── tsconfig.json
└── vue1-test.html
Download .txt
SYMBOL INDEX (775 symbols across 146 files)

FILE: cypress/utils/suite.js
  function suite (line 1) | function suite(description, tests) {

FILE: extension-zips.js
  constant IS_CI (line 7) | const IS_CI = !!(process.env.CIRCLECI || process.env.GITHUB_ACTIONS)
  constant INCLUDE_GLOBS (line 11) | const INCLUDE_GLOBS = [
  constant SKIP_DIR_GLOBS (line 21) | const SKIP_DIR_GLOBS = ['node_modules', 'src']
  function bytesToSize (line 23) | function bytesToSize(bytes) {
  function writeZip (line 36) | async function writeZip(fileName, packageDir) {

FILE: packages/api/src/api/api.ts
  type DevtoolsPluginApi (line 7) | interface DevtoolsPluginApi<TSettings> {
  type AppRecord (line 29) | interface AppRecord {
  type TimelineLayerOptions (line 36) | interface TimelineLayerOptions<TData = any, TMeta = any> {
  type ScreenshotOverlayEvent (line 46) | interface ScreenshotOverlayEvent {
  type ScreenshotOverlayRenderContext (line 51) | interface ScreenshotOverlayRenderContext<TData = any, TMeta = any> {
  type ScreenshotOverlayRenderResult (line 57) | type ScreenshotOverlayRenderResult = HTMLElement | string | false
  type ScreenshotData (line 59) | interface ScreenshotData {
  type TimelineEventOptions (line 63) | interface TimelineEventOptions {
  type TimelineEvent (line 69) | interface TimelineEvent<TData = any, TMeta = any> {
  type TimelineMarkerOptions (line 79) | interface TimelineMarkerOptions {
  type CustomInspectorOptions (line 87) | interface CustomInspectorOptions {
  type CustomInspectorNode (line 106) | interface CustomInspectorNode {
  type InspectorNodeTag (line 113) | interface InspectorNodeTag {
  type CustomInspectorState (line 120) | interface CustomInspectorState {

FILE: packages/api/src/api/app.ts
  type App (line 1) | type App = any // @TODO

FILE: packages/api/src/api/component.ts
  type ComponentInstance (line 4) | type ComponentInstance = any // @TODO
  type ComponentTreeNode (line 6) | interface ComponentTreeNode {
  type InspectedComponentData (line 24) | interface InspectedComponentData {
  type StateBase (line 32) | interface StateBase {
  type ComponentStateBase (line 40) | interface ComponentStateBase extends StateBase {
  type ComponentPropState (line 44) | interface ComponentPropState extends ComponentStateBase {
  type ComponentBuiltinCustomStateTypes (line 53) | type ComponentBuiltinCustomStateTypes = 'function' | 'map' | 'set' | 're...
  type ComponentCustomState (line 55) | interface ComponentCustomState extends ComponentStateBase {
  type CustomState (line 59) | interface CustomState {
  type ComponentState (line 85) | type ComponentState = ComponentStateBase | ComponentPropState | Componen...
  type ComponentDevtoolsOptions (line 87) | interface ComponentDevtoolsOptions {

FILE: packages/api/src/api/context.ts
  type Context (line 3) | interface Context {

FILE: packages/api/src/api/hooks.ts
  type Hooks (line 6) | const enum Hooks {
  type ComponentBounds (line 31) | interface ComponentBounds {
  type HookPayloads (line 38) | interface HookPayloads {
  type EditStatePayload (line 152) | type EditStatePayload = {
  type HookHandler (line 162) | type HookHandler<TPayload, TContext> = (payload: TPayload, ctx: TContext...
  type Hookable (line 164) | interface Hookable<TContext> {

FILE: packages/api/src/api/util.ts
  type ID (line 1) | type ID = number | string
  type WithId (line 3) | interface WithId {

FILE: packages/api/src/const.ts
  constant HOOK_SETUP (line 1) | const HOOK_SETUP = 'devtools-plugin:setup'
  constant HOOK_PLUGIN_SETTINGS_SET (line 2) | const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set'

FILE: packages/api/src/env.ts
  type PluginQueueItem (line 4) | interface PluginQueueItem {
  type GlobalTarget (line 10) | interface GlobalTarget {
  function getDevtoolsGlobalHook (line 15) | function getDevtoolsGlobalHook(): any {
  function getTarget (line 19) | function getTarget(): GlobalTarget {

FILE: packages/api/src/index.ts
  type Cast (line 13) | type Cast<A, B> = A extends B ? A : B
  type Narrowable (line 14) | type Narrowable =
  type Narrow (line 19) | type Narrow<A> = Cast<A, | []
  type Exact (line 25) | type Exact<C, T> = {
  type SetupFunction (line 29) | type SetupFunction<TSettings = any> = (api: DevtoolsPluginApi<TSettings>...
  function setupDevtoolsPlugin (line 31) | function setupDevtoolsPlugin<

FILE: packages/api/src/plugin.ts
  type PluginDescriptor (line 3) | interface PluginDescriptor {
  type PluginSettingsItem (line 21) | type PluginSettingsItem = {
  type InferSettingsType (line 37) | type InferSettingsType<
  type ExtractSettingsTypes (line 47) | type ExtractSettingsTypes<

FILE: packages/api/src/proxy.ts
  type QueueItem (line 6) | interface QueueItem {
  class ApiProxy (line 12) | class ApiProxy<TTarget extends DevtoolsPluginApi<any> = DevtoolsPluginAp...
    method constructor (line 24) | constructor(plugin: PluginDescriptor, hook: any) {
    method setRealTarget (line 125) | async setRealTarget(target: TTarget) {

FILE: packages/api/src/time.ts
  function isPerformanceSupported (line 4) | function isPerformanceSupported() {
  function now (line 22) | function now() {

FILE: packages/app-backend-api/src/api.ts
  class DevtoolsApi (line 38) | class DevtoolsApi {
    method constructor (line 45) | constructor(backend: DevtoolsBackend, ctx: BackendContext) {
    method callHook (line 52) | async callHook<T extends Hooks>(eventType: T, payload: HookPayloads[T]...
    method transformCall (line 60) | async transformCall(callName: string, ...args) {
    method getAppRecordName (line 69) | async getAppRecordName(app: App, defaultName: string): Promise<string> {
    method getAppRootInstance (line 82) | async getAppRootInstance(app: App) {
    method registerApplication (line 90) | async registerApplication(app: App) {
    method walkComponentTree (line 96) | async walkComponentTree(instance: ComponentInstance, maxDepth = -1, fi...
    method visitComponentTree (line 107) | async visitComponentTree(instance: ComponentInstance, treeNode: Compon...
    method walkComponentParents (line 117) | async walkComponentParents(instance: ComponentInstance) {
    method inspectComponent (line 125) | async inspectComponent(instance: ComponentInstance, app: App) {
    method getComponentBounds (line 134) | async getComponentBounds(instance: ComponentInstance) {
    method getComponentName (line 142) | async getComponentName(instance: ComponentInstance) {
    method getComponentInstances (line 150) | async getComponentInstances(app: App) {
    method getElementComponent (line 158) | async getElementComponent(element: HTMLElement | any) {
    method getComponentRootElements (line 166) | async getComponentRootElements(instance: ComponentInstance) {
    method editComponentState (line 174) | async editComponentState(instance: ComponentInstance, dotPath: string,...
    method getComponentDevtoolsOptions (line 187) | async getComponentDevtoolsOptions(instance: ComponentInstance): Promis...
    method getComponentRenderCode (line 195) | async getComponentRenderCode(instance: ComponentInstance): Promise<{
    method inspectTimelineEvent (line 207) | async inspectTimelineEvent(eventData: TimelineEventOptions & WithId, a...
    method clearTimeline (line 218) | async clearTimeline() {
    method getInspectorTree (line 222) | async getInspectorTree(inspectorId: string, app: App, filter: string) {
    method getInspectorState (line 232) | async getInspectorState(inspectorId: string, app: App, nodeId: string) {
    method editInspectorState (line 242) | async editInspectorState(inspectorId: string, app: App, nodeId: string...
    method now (line 255) | now() {
  class DevtoolsPluginApiInstance (line 260) | class DevtoolsPluginApiInstance<TSettings = any> implements DevtoolsPlug...
    method constructor (line 269) | constructor(plugin: Plugin, appRecord: AppRecord, ctx: BackendContext) {
    method notifyComponentUpdate (line 282) | async notifyComponentUpdate(instance: ComponentInstance = null) {
    method addTimelineLayer (line 295) | addTimelineLayer(options: TimelineLayerOptions) {
    method addTimelineEvent (line 304) | addTimelineEvent(options: TimelineEventOptions) {
    method addInspector (line 313) | addInspector(options: CustomInspectorOptions) {
    method sendInspectorTree (line 322) | sendInspectorTree(inspectorId: string) {
    method sendInspectorState (line 331) | sendInspectorState(inspectorId: string) {
    method selectInspectorNode (line 340) | selectInspectorNode(inspectorId: string, nodeId: string) {
    method getComponentBounds (line 349) | getComponentBounds(instance: ComponentInstance) {
    method getComponentName (line 353) | getComponentName(instance: ComponentInstance) {
    method getComponentInstances (line 357) | getComponentInstances(app: App) {
    method highlightElement (line 361) | highlightElement(instance: ComponentInstance) {
    method unhighlightElement (line 370) | unhighlightElement() {
    method getSettings (line 379) | getSettings(pluginId?: string) {
    method setSettings (line 383) | setSettings(value: TSettings, pluginId?: string) {
    method now (line 387) | now() {
    method enabled (line 391) | private get enabled() {
    method hasPermission (line 395) | private hasPermission(permission: PluginPermission) {

FILE: packages/app-backend-api/src/app-record.ts
  type AppRecordOptions (line 4) | interface AppRecordOptions {
  type AppRecord (line 11) | interface AppRecord {
  type SimpleAppRecord (line 29) | interface SimpleAppRecord {

FILE: packages/app-backend-api/src/backend-context.ts
  type BackendContext (line 15) | interface BackendContext {
  type TimelineLayer (line 33) | interface TimelineLayer extends TimelineLayerOptions {
  type TimelineMarker (line 39) | interface TimelineMarker extends TimelineMarkerOptions {
  type CustomInspector (line 43) | interface CustomInspector extends CustomInspectorOptions {
  type CreateBackendContextOptions (line 50) | interface CreateBackendContextOptions {
  function createBackendContext (line 55) | function createBackendContext(options: CreateBackendContextOptions): Bac...

FILE: packages/app-backend-api/src/backend.ts
  type BuiltinBackendFeature (line 5) | enum BuiltinBackendFeature {
  type DevtoolsBackendOptions (line 12) | interface DevtoolsBackendOptions {
  function defineBackend (line 19) | function defineBackend(options: DevtoolsBackendOptions) {
  type DevtoolsBackend (line 23) | interface DevtoolsBackend {
  function createBackend (line 28) | function createBackend(options: DevtoolsBackendOptions, ctx: BackendCont...

FILE: packages/app-backend-api/src/global-hook.ts
  type DevtoolsHook (line 3) | interface DevtoolsHook {

FILE: packages/app-backend-api/src/hooks.ts
  type Handler (line 7) | type Handler<TPayload> = HookHandler<TPayload, BackendContext>
  type HookHandlerData (line 9) | interface HookHandlerData<THandlerPayload> {
  class DevtoolsHookable (line 14) | class DevtoolsHookable implements Hookable<BackendContext> {
    method constructor (line 19) | constructor(ctx: BackendContext, plugin: Plugin = null) {
    method hook (line 24) | private hook<T extends Hooks>(eventType: T, handler: Handler<HookPaylo...
    method callHandlers (line 53) | async callHandlers<T extends Hooks>(eventType: T, payload: HookPayload...
    method transformCall (line 72) | transformCall(handler: Handler<HookPayloads[Hooks.TRANSFORM_CALL]>) {
    method getAppRecordName (line 76) | getAppRecordName(handler: Handler<HookPayloads[Hooks.GET_APP_RECORD_NA...
    method getAppRootInstance (line 80) | getAppRootInstance(handler: Handler<HookPayloads[Hooks.GET_APP_ROOT_IN...
    method registerApplication (line 84) | registerApplication(handler: Handler<HookPayloads[Hooks.REGISTER_APPLI...
    method walkComponentTree (line 88) | walkComponentTree(handler: Handler<HookPayloads[Hooks.WALK_COMPONENT_T...
    method visitComponentTree (line 92) | visitComponentTree(handler: Handler<HookPayloads[Hooks.VISIT_COMPONENT...
    method walkComponentParents (line 96) | walkComponentParents(handler: Handler<HookPayloads[Hooks.WALK_COMPONEN...
    method inspectComponent (line 100) | inspectComponent(handler: Handler<HookPayloads[Hooks.INSPECT_COMPONENT...
    method getComponentBounds (line 104) | getComponentBounds(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_B...
    method getComponentName (line 108) | getComponentName(handler: Handler<HookPayloads[Hooks.GET_COMPONENT_NAM...
    method getComponentInstances (line 112) | getComponentInstances(handler: Handler<HookPayloads[Hooks.GET_COMPONEN...
    method getElementComponent (line 116) | getElementComponent(handler: Handler<HookPayloads[Hooks.GET_ELEMENT_CO...
    method getComponentRootElements (line 120) | getComponentRootElements(handler: Handler<HookPayloads[Hooks.GET_COMPO...
    method editComponentState (line 124) | editComponentState(handler: Handler<HookPayloads[Hooks.EDIT_COMPONENT_...
    method getComponentDevtoolsOptions (line 128) | getComponentDevtoolsOptions(handler: Handler<HookPayloads[Hooks.GET_CO...
    method getComponentRenderCode (line 132) | getComponentRenderCode(handler: Handler<HookPayloads[Hooks.GET_COMPONE...
    method inspectTimelineEvent (line 136) | inspectTimelineEvent(handler: Handler<HookPayloads[Hooks.INSPECT_TIMEL...
    method timelineCleared (line 140) | timelineCleared(handler: Handler<HookPayloads[Hooks.TIMELINE_CLEARED]>) {
    method getInspectorTree (line 144) | getInspectorTree(handler: Handler<HookPayloads[Hooks.GET_INSPECTOR_TRE...
    method getInspectorState (line 148) | getInspectorState(handler: Handler<HookPayloads[Hooks.GET_INSPECTOR_ST...
    method editInspectorState (line 152) | editInspectorState(handler: Handler<HookPayloads[Hooks.EDIT_INSPECTOR_...
    method setPluginSettings (line 156) | setPluginSettings(handler: Handler<HookPayloads[Hooks.SET_PLUGIN_SETTI...

FILE: packages/app-backend-api/src/plugin.ts
  type Plugin (line 3) | interface Plugin {

FILE: packages/app-backend-core/src/app.ts
  type AppRecordResolver (line 22) | type AppRecordResolver = (record: AppRecord) => void | Promise<void>
  function registerApp (line 25) | async function registerApp(options: AppRecordOptions, ctx: BackendContex...
  function registerAppJob (line 29) | async function registerAppJob(options: AppRecordOptions, ctx: BackendCon...
  function createAppRecord (line 54) | async function createAppRecord(options: AppRecordOptions, backend: Devto...
  function selectApp (line 139) | async function selectApp(record: AppRecord, ctx: BackendContext) {
  function mapAppRecord (line 148) | function mapAppRecord(record: AppRecord): SimpleAppRecord {
  function getAppRecordId (line 159) | function getAppRecordId(app, defaultId?: string): string {
  function getAppRecord (line 179) | async function getAppRecord(app: any, ctx: BackendContext): Promise<AppR...
  function waitForAppsRegistration (line 217) | function waitForAppsRegistration() {
  function sendApps (line 221) | async function sendApps(ctx: BackendContext) {
  function removeAppRecord (line 233) | function removeAppRecord(appRecord: AppRecord, ctx: BackendContext) {
  function removeApp (line 250) | async function removeApp(app: App, ctx: BackendContext) {
  function _legacy_getAndRegisterApps (line 266) | function _legacy_getAndRegisterApps(ctx: BackendContext, clear = false) {

FILE: packages/app-backend-core/src/backend.ts
  function getBackend (line 18) | function getBackend(backendOptions: DevtoolsBackendOptions, ctx: Backend...

FILE: packages/app-backend-core/src/component-pick.ts
  class ComponentPicker (line 6) | class ComponentPicker {
    method constructor (line 11) | constructor(ctx: BackendContext) {
    method startSelecting (line 19) | startSelecting() {
    method stopSelecting (line 35) | stopSelecting() {
    method elementMouseOver (line 53) | async elementMouseOver(e: MouseEvent) {
    method selectElementComponent (line 67) | async selectElementComponent(el) {
    method elementClicked (line 83) | async elementClicked(e: MouseEvent) {
    method cancelEvent (line 100) | cancelEvent(e: MouseEvent) {
    method bindMethods (line 108) | bindMethods() {

FILE: packages/app-backend-core/src/component.ts
  function sendComponentTreeData (line 10) | async function sendComponentTreeData(appRecord: AppRecord, instanceId: s...
  function sendSelectedComponentData (line 48) | async function sendSelectedComponentData(appRecord: AppRecord, instanceI...
  function markSelectedInstance (line 88) | function markSelectedInstance(instanceId: string, ctx: BackendContext) {
  function sendEmptyComponentData (line 93) | function sendEmptyComponentData(instanceId: string, ctx: BackendContext) {
  function editComponentState (line 100) | async function editComponentState(instanceId: string, dotPath: string, t...
  function getComponentId (line 114) | async function getComponentId(app: App, uid: number, instance: Component...
  function getComponentInstance (line 134) | function getComponentInstance(appRecord: AppRecord, instanceId: string, ...
  function refreshComponentTreeSearch (line 148) | async function refreshComponentTreeSearch(ctx: BackendContext) {
  function sendComponentUpdateTracking (line 157) | function sendComponentUpdateTracking(instanceId: string, time: number, c...

FILE: packages/app-backend-core/src/flash.ts
  function flashComponent (line 4) | async function flashComponent(instance: ComponentInstance, backend: Devt...

FILE: packages/app-backend-core/src/highlighter.ts
  function createOverlay (line 10) | function createOverlay() {
  function highlight (line 39) | async function highlight(instance: ComponentInstance, backend: DevtoolsB...
  function unHighlight (line 82) | async function unHighlight() {
  function showOverlay (line 92) | function showOverlay(bounds: ComponentBounds, children: Node[] = null) {
  function positionOverlay (line 107) | function positionOverlay({ width = 0, height = 0, top = 0, left = 0 }) {
  function positionOverlayContent (line 114) | function positionOverlayContent({ height = 0, top = 0, left = 0 }) {
  function updateOverlay (line 139) | async function updateOverlay(backend: DevtoolsBackend, _ctx: BackendCont...
  function startUpdateTimer (line 157) | function startUpdateTimer(backend: DevtoolsBackend, ctx: BackendContext) {
  function stopUpdateTimer (line 166) | function stopUpdateTimer() {

FILE: packages/app-backend-core/src/hook.ts
  function installHook (line 11) | function installHook(target, isIframe = false) {

FILE: packages/app-backend-core/src/index.ts
  function initBackend (line 60) | async function initBackend(bridge: Bridge) {
  function connect (line 109) | async function connect() {
  function connectBridge (line 464) | function connectBridge() {

FILE: packages/app-backend-core/src/inspector.ts
  function getInspector (line 5) | function getInspector(inspectorId: string, app: App, ctx: BackendContext) {
  function getInspectorWithAppId (line 9) | async function getInspectorWithAppId(inspectorId: string, appId: string,...
  function sendInspectorTree (line 18) | async function sendInspectorTree(inspector: CustomInspector, ctx: Backen...
  function sendInspectorState (line 27) | async function sendInspectorState(inspector: CustomInspector, ctx: Backe...
  function editInspectorState (line 36) | async function editInspectorState(inspector: CustomInspector, nodeId: st...
  function sendCustomInspectors (line 43) | async function sendCustomInspectors(ctx: BackendContext) {
  function selectInspectorNode (line 70) | async function selectInspectorNode(inspector: CustomInspector, nodeId: s...

FILE: packages/app-backend-core/src/legacy/scan.ts
  function scan (line 9) | function scan() {
  function walk (line 93) | function walk(node, fn) {

FILE: packages/app-backend-core/src/page-config.ts
  type PageConfig (line 3) | interface PageConfig {
  function getPageConfig (line 11) | function getPageConfig(): PageConfig {
  function initOnPageConfig (line 15) | function initOnPageConfig() {

FILE: packages/app-backend-core/src/perf.ts
  function performanceMarkStart (line 17) | async function performanceMarkStart(
  function performanceMarkEnd (line 78) | async function performanceMarkEnd(
  function handleAddPerformanceTag (line 172) | function handleAddPerformanceTag(backend: DevtoolsBackend, _ctx: Backend...

FILE: packages/app-backend-core/src/plugin.ts
  function addPlugin (line 7) | async function addPlugin(pluginQueueItem: PluginQueueItem, ctx: BackendC...
  function addQueuedPlugins (line 48) | async function addQueuedPlugins(ctx: BackendContext) {
  function addPreviouslyRegisteredPlugins (line 57) | async function addPreviouslyRegisteredPlugins(ctx: BackendContext) {
  function sendPluginList (line 65) | async function sendPluginList(ctx: BackendContext) {
  function serializePlugin (line 71) | async function serializePlugin(plugin: Plugin) {

FILE: packages/app-backend-core/src/timeline-builtins.ts
  method screenshotOverlayRender (line 8) | screenshotOverlayRender(event, { events }) {

FILE: packages/app-backend-core/src/timeline-marker.ts
  function addTimelineMarker (line 7) | async function addTimelineMarker(options: TimelineMarkerOptions, ctx: Ba...
  function sendTimelineMarkers (line 25) | async function sendTimelineMarkers(ctx: BackendContext) {
  function serializeMarker (line 43) | async function serializeMarker(marker: TimelineMarker) {

FILE: packages/app-backend-core/src/timeline-screenshot.ts
  type Screenshot (line 13) | interface Screenshot {
  function showScreenshot (line 20) | async function showScreenshot(screenshot: Screenshot, ctx: BackendContex...
  function createElements (line 78) | function createElements() {
  function showElement (line 108) | function showElement() {
  function hideElement (line 115) | function hideElement() {
  function clearContent (line 125) | function clearContent() {

FILE: packages/app-backend-core/src/timeline.ts
  function setupTimeline (line 9) | function setupTimeline(ctx: BackendContext) {
  function addBuiltinLayers (line 13) | function addBuiltinLayers(appRecord: AppRecord, ctx: BackendContext) {
  function setupBuiltinLayers (line 24) | function setupBuiltinLayers(ctx: BackendContext) {
  function sendTimelineLayers (line 114) | async function sendTimelineLayers(ctx: BackendContext) {
  function addTimelineEvent (line 140) | async function addTimelineEvent(options: TimelineEventOptions, app: App,...
  function mapTimelineEvent (line 175) | function mapTimelineEvent(eventData: TimelineEventOptions & WithId) {
  function clearTimeline (line 190) | async function clearTimeline(ctx: BackendContext) {
  function sendTimelineEventData (line 200) | async function sendTimelineEventData(id: ID, ctx: BackendContext) {
  function removeLayersForApp (line 219) | function removeLayersForApp(app: App, ctx: BackendContext) {
  function sendTimelineLayerEvents (line 232) | function sendTimelineLayerEvents(appId: string, layerId: string, ctx: Ba...

FILE: packages/app-backend-core/src/toast.ts
  function installToast (line 1) | function installToast() {

FILE: packages/app-backend-core/src/util/queue.ts
  type Job (line 1) | interface Job {
  class JobQueue (line 6) | class JobQueue {
    method queue (line 10) | queue(id: string, fn: Job['fn']) {

FILE: packages/app-backend-core/src/util/subscriptions.ts
  function getSubs (line 3) | function getSubs(type: string) {
  function subscribe (line 12) | function subscribe(type: string, key: string) {
  function unsubscribe (line 16) | function unsubscribe(type: string, key: string) {
  function isSubscribed (line 21) | function isSubscribed(

FILE: packages/app-backend-vue1/src/index.ts
  method setup (line 6) | setup(_api) {

FILE: packages/app-backend-vue2/src/components/data.ts
  function getInstanceDetails (line 10) | function getInstanceDetails(instance): InspectedComponentData {
  function getInstanceState (line 55) | function getInstanceState(instance): ComponentState[] {
  function getFunctionalInstanceState (line 70) | function getFunctionalInstanceState(instance): ComponentState[] {
  function getCustomInstanceDetails (line 74) | function getCustomInstanceDetails(instance) {
  function reduceStateList (line 90) | function reduceStateList(list) {
  function getInstanceName (line 105) | function getInstanceName(instance): string {
  function processProps (line 120) | function processProps(instance): ComponentState[] {
  function processAttrs (line 144) | function processAttrs(instance): ComponentState[] {
  function getPropType (line 159) | function getPropType(type) {
  function processState (line 177) | function processState(instance): ComponentState[] {
  function processSetupState (line 195) | function processSetupState(instance) {
  function returnError (line 241) | function returnError(cb: () => any) {
  function isRef (line 250) | function isRef(raw: any): boolean {
  function isComputed (line 254) | function isComputed(raw: any): boolean {
  function isReactive (line 258) | function isReactive(raw: any): boolean {
  function isReadOnly (line 262) | function isReadOnly(raw: any): boolean {
  function toRaw (line 266) | function toRaw(value: any) {
  function getSetupStateInfo (line 273) | function getSetupStateInfo(raw: any) {
  function getCustomObjectDetails (line 282) | function getCustomObjectDetails(object: any, _proto: string): CustomStat...
  function processRefs (line 304) | function processRefs(instance): ComponentState[] {
  function processComputed (line 313) | function processComputed(instance): ComponentState[] {
  function processInjected (line 352) | function processInjected(instance): ComponentState[] {
  function processRouteContext (line 372) | function processRouteContext(instance): ComponentState[] {
  function processVuexGetters (line 412) | function processVuexGetters(instance): ComponentState[] {
  function processFirebaseBindings (line 433) | function processFirebaseBindings(instance): ComponentState[] {
  function processObservables (line 452) | function processObservables(instance): ComponentState[] {
  function findInstanceOrVnode (line 468) | function findInstanceOrVnode(id) {
  function editState (line 477) | function editState(

FILE: packages/app-backend-vue2/src/components/el.ts
  function createRect (line 3) | function createRect() {
  function mergeRects (line 15) | function mergeRects(a, b) {
  function getInstanceOrVnodeRect (line 34) | function getInstanceOrVnodeRect(instance) {
  function getLegacyFragmentRect (line 58) | function getLegacyFragmentRect({ _fragmentStart, _fragmentEnd }) {
  function getTextRect (line 79) | function getTextRect(node: Text) {
  function util (line 95) | function util() {
  function findRelatedComponent (line 99) | function findRelatedComponent(el) {
  function getElWindow (line 106) | function getElWindow(el: HTMLElement) {
  function addIframePosition (line 110) | function addIframePosition(bounds, win: any) {
  function getRootElementsFromComponentInstance (line 126) | function getRootElementsFromComponentInstance(instance) {

FILE: packages/app-backend-vue2/src/components/perf.ts
  constant COMPONENT_HOOKS (line 5) | const COMPONENT_HOOKS = {
  function initPerf (line 16) | function initPerf(api: DevtoolsApi, app, Vue) {
  function applyPerfHooks (line 28) | function applyPerfHooks(api: DevtoolsApi, vm, app) {

FILE: packages/app-backend-vue2/src/components/tree.ts
  function getInstanceMap (line 12) | function getInstanceMap() {
  function getFunctionalVnodeMap (line 16) | function getFunctionalVnodeMap() {
  function walkTree (line 33) | async function walkTree(instance, pFilter: string, pRecursively: boolean...
  function getComponentParents (line 43) | function getComponentParents(instance, api: DevtoolsApi, ctx: BackendCon...
  function initCtx (line 72) | function initCtx(_api: DevtoolsApi, ctx: BackendContext) {
  function findQualifiedChildrenFromList (line 94) | function findQualifiedChildrenFromList(instances: any[]): Promise<Compon...
  function findQualifiedChildren (line 107) | async function findQualifiedChildren(instance): Promise<ComponentTreeNod...
  function getInternalInstanceChildren (line 129) | function getInternalInstanceChildren(instance): any[] {
  function isQualified (line 139) | function isQualified(instance): boolean {
  function flatten (line 145) | function flatten<T>(items: any[]): T[] {
  function captureChild (line 168) | function captureChild(child): Promise<ComponentTreeNode[] | ComponentTre...
  function capture (line 185) | async function capture(instance, _index?: number, _list?: any[]): Promis...
  function mark (line 349) | function mark(instance) {
  function markFunctional (line 362) | function markFunctional(id, vnode) {

FILE: packages/app-backend-vue2/src/components/update-tracking.ts
  function initUpdateTracking (line 6) | function initUpdateTracking(api: DevtoolsApi, Vue) {
  constant COMPONENT_HOOKS (line 15) | const COMPONENT_HOOKS = [
  function applyTrackingUpdateHook (line 20) | function applyTrackingUpdateHook(api: DevtoolsApi, vm) {

FILE: packages/app-backend-vue2/src/components/util.ts
  function isBeingDestroyed (line 4) | function isBeingDestroyed(instance) {
  function getInstanceName (line 11) | function getInstanceName(instance) {
  function getRenderKey (line 21) | function getRenderKey(value): string {
  function getUniqueId (line 43) | function getUniqueId(instance, appRecord?: AppRecord): string {

FILE: packages/app-backend-vue2/src/events.ts
  function wrap (line 6) | function wrap(app, Vue, method, ctx: BackendContext) {
  function wrapVueForEvents (line 28) | function wrapVueForEvents(app, Vue, ctx: BackendContext) {

FILE: packages/app-backend-vue2/src/index.ts
  method setup (line 18) | setup(api) {
  method setupApp (line 79) | setupApp(api, appRecord) {
  function injectToUtils (line 110) | function injectToUtils() {

FILE: packages/app-backend-vue2/src/plugin.ts
  constant VUEX_ROOT_PATH (line 9) | const VUEX_ROOT_PATH = '__vdt_root'
  constant VUEX_MODULE_PATH_SEPARATOR (line 10) | const VUEX_MODULE_PATH_SEPARATOR = '[vdt]'
  constant VUEX_MODULE_PATH_SEPARATOR_REG (line 11) | const VUEX_MODULE_PATH_SEPARATOR_REG = /\[vdt\]/g
  constant BLUE_600 (line 16) | const BLUE_600 = 0x2563EB
  constant LIME_500 (line 17) | const LIME_500 = 0x84CC16
  constant CYAN_400 (line 18) | const CYAN_400 = 0x22D3EE
  constant ORANGE_400 (line 19) | const ORANGE_400 = 0xFB923C
  constant WHITE (line 20) | const WHITE = 0xFFFFFF
  constant DARK (line 21) | const DARK = 0x666666
  function setupPlugin (line 23) | function setupPlugin(api: DevtoolsApi, app: App, Vue) {
  function formatRouteNode (line 295) | function formatRouteNode(router, route, parentPath: string, filter: stri...
  function formatRouteData (line 344) | function formatRouteData(route) {
  function getPathId (line 384) | function getPathId(routeMatcher) {
  constant TAG_NAMESPACED (line 392) | const TAG_NAMESPACED = {
  function formatStoreForInspectorTree (line 398) | function formatStoreForInspectorTree(module, moduleName: string, path: s...
  function flattenStoreForInspectorTree (line 416) | function flattenStoreForInspectorTree(result: CustomInspectorNode[], mod...
  function extractNameFromPath (line 429) | function extractNameFromPath(path: string) {
  function formatStoreForInspectorState (line 433) | function formatStoreForInspectorState(module, getters, path): CustomInsp...
  function transformPathsToObjectTree (line 476) | function transformPathsToObjectTree(getters) {
  function getStoreModule (line 505) | function getStoreModule(moduleMap, path) {
  function canThrow (line 527) | function canThrow(cb: () => any) {

FILE: packages/app-backend-vue3/src/components/data.ts
  function getInstanceDetails (line 65) | function getInstanceDetails(instance: any, ctx: BackendContext): Inspect...
  function getInstanceState (line 74) | function getInstanceState(instance) {
  function processProps (line 96) | function processProps(instance) {
  function getPropType (line 130) | function getPropType(type) {
  function processState (line 152) | function processState(instance) {
  function processSetupState (line 179) | function processSetupState(instance) {
  function isRef (line 230) | function isRef(raw: any): boolean {
  function isComputed (line 234) | function isComputed(raw: any): boolean {
  function isReactive (line 238) | function isReactive(raw: any): boolean {
  function isReadOnly (line 242) | function isReadOnly(raw: any): boolean {
  function toRaw (line 246) | function toRaw(value: any) {
  function getSetupStateInfo (line 253) | function getSetupStateInfo(raw: any) {
  function getCustomObjectDetails (line 262) | function getCustomObjectDetails(object: any, _proto: string): CustomStat...
  function processComputed (line 296) | function processComputed(instance, mergedType) {
  function processAttrs (line 320) | function processAttrs(instance) {
  function processProvide (line 329) | function processProvide(instance) {
  function processInject (line 338) | function processInject(instance, mergedType) {
  function processRefs (line 374) | function processRefs(instance) {
  function processEventListeners (line 383) | function processEventListeners(instance) {
  function editState (line 412) | function editState({ componentInstance, path, state, type }: HookPayload...
  function reduceStateList (line 444) | function reduceStateList(list) {
  function getCustomInstanceDetails (line 456) | function getCustomInstanceDetails(instance) {
  function resolveMergedOptions (line 475) | function resolveMergedOptions(
  function mergeOptions (line 490) | function mergeOptions(

FILE: packages/app-backend-vue3/src/components/el.ts
  function getComponentInstanceFromElement (line 4) | function getComponentInstanceFromElement(element) {
  function getRootElementsFromComponentInstance (line 8) | function getRootElementsFromComponentInstance(instance) {
  function getFragmentRootElements (line 18) | function getFragmentRootElements(vnode): any[] {
  function getInstanceOrVnodeRect (line 44) | function getInstanceOrVnodeRect(instance) {
  function createRect (line 66) | function createRect() {
  function mergeRects (line 78) | function mergeRects(a, b) {
  function getTextRect (line 101) | function getTextRect(node) {
  function getFragmentRect (line 114) | function getFragmentRect(vnode) {
  function getElWindow (line 143) | function getElWindow(el: HTMLElement) {
  function addIframePosition (line 147) | function addIframePosition(bounds, win: any) {

FILE: packages/app-backend-vue3/src/components/filter.ts
  class ComponentFilter (line 4) | class ComponentFilter {
    method constructor (line 7) | constructor(filter: string) {
    method isQualified (line 17) | isQualified(instance) {

FILE: packages/app-backend-vue3/src/components/tree.ts
  class ComponentWalker (line 7) | class ComponentWalker {
    method constructor (line 17) | constructor(maxDepth: number, filter: string, recursively: boolean, ap...
    method getComponentTree (line 25) | getComponentTree(instance: any): Promise<ComponentTreeNode[]> {
    method getComponentParents (line 30) | getComponentParents(instance: any) {
    method findQualifiedChildren (line 51) | private async findQualifiedChildren(instance: any, depth: number): Pro...
    method findQualifiedChildrenFromList (line 76) | private async findQualifiedChildrenFromList(instances, depth: number):...
    method getInternalInstanceChildren (line 90) | private getInternalInstanceChildren(subTree, suspense = null) {
    method captureId (line 114) | private captureId(instance): string {
    method capture (line 144) | private async capture(instance: any, list: any[], depth: number): Prom...
    method mark (line 238) | private mark(instance, force = false) {
    method isKeepAlive (line 245) | private isKeepAlive(instance) {
    method getKeepAliveCachedInstances (line 249) | private getKeepAliveCachedInstances(instance) {

FILE: packages/app-backend-vue3/src/components/util.ts
  function isBeingDestroyed (line 5) | function isBeingDestroyed(instance) {
  function getAppRecord (line 9) | function getAppRecord(instance) {
  function isFragment (line 15) | function isFragment(instance) {
  function getInstanceName (line 28) | function getInstanceName(instance) {
  function saveComponentName (line 53) | function saveComponentName(instance, key) {
  function getComponentTypeName (line 58) | function getComponentTypeName(options) {
  function getComponentFileName (line 62) | function getComponentFileName(options) {
  function getUniqueComponentId (line 73) | function getUniqueComponentId(instance, _ctx: BackendContext) {
  function getRenderKey (line 79) | function getRenderKey(value): string {
  function getComponentInstances (line 98) | function getComponentInstances(app: App): ComponentInstance[] {

FILE: packages/app-backend-vue3/src/index.ts
  method setup (line 13) | setup(api) {

FILE: packages/app-backend-vue3/src/util.ts
  function flatten (line 1) | function flatten(items) {
  function returnError (line 14) | function returnError(cb: () => any) {

FILE: packages/app-frontend/src/app.ts
  function createApp (line 18) | function createApp() {
  function connectApp (line 42) | function connectApp(app: VueApp, shell) {

FILE: packages/app-frontend/src/features/apps/index.ts
  type AppRecord (line 8) | interface AppRecord {
  function useCurrentApp (line 17) | function useCurrentApp() {
  function useApps (line 28) | function useApps() {
  function addApp (line 55) | function addApp(app: AppRecord) {
  function removeApp (line 60) | function removeApp(appId: string) {
  function getApps (line 67) | function getApps() {
  function fetchApps (line 71) | function fetchApps() {
  function waitForAppSelect (line 79) | function waitForAppSelect(): Promise<void> {
  function scanLegacyApps (line 90) | function scanLegacyApps() {
  function setupAppsBridgeEvents (line 94) | function setupAppsBridgeEvents(bridge: Bridge) {

FILE: packages/app-frontend/src/features/apps/vue-version-check.ts
  function useVueVersionCheck (line 6) | function useVueVersionCheck() {

FILE: packages/app-frontend/src/features/bridge/index.ts
  type Sub (line 7) | interface Sub {
  function useBridge (line 12) | function useBridge() {
  function setBridge (line 52) | function setBridge(b: Bridge) {
  function getBridge (line 56) | function getBridge(): Bridge | null {

FILE: packages/app-frontend/src/features/chrome/pane-visibility.ts
  function ensurePaneShown (line 17) | function ensurePaneShown(cb: () => void | Promise<void>) {
  function onPanelShown (line 26) | function onPanelShown() {
  function onPanelHidden (line 34) | function onPanelHidden() {

FILE: packages/app-frontend/src/features/components/composable/components.ts
  function useComponentRequests (line 31) | function useComponentRequests() {
  function useComponents (line 54) | function useComponents() {
  function useComponent (line 132) | function useComponent(instance: Ref<ComponentTreeNode>) {
  function setComponentOpen (line 202) | function setComponentOpen(id: ComponentTreeNode['id'], isOpen: boolean) {
  function isComponentOpen (line 206) | function isComponentOpen(id: ComponentTreeNode['id']) {
  function useSelectedComponent (line 210) | function useSelectedComponent() {
  function resetComponents (line 282) | function resetComponents() {
  function requestComponentTree (line 294) | async function requestComponentTree(instanceId: ComponentTreeNode['id'] ...
  function _sendTreeRequest (line 310) | function _sendTreeRequest(instanceId: ComponentTreeNode['id'], recursive...
  function _queueRetryTree (line 318) | function _queueRetryTree(instanceId: ComponentTreeNode['id'], recursivel...
  function _retryRequestComponentTree (line 323) | function _retryRequestComponentTree(instanceId: ComponentTreeNode['id'],...
  function ensureComponentsMapData (line 332) | function ensureComponentsMapData(data: ComponentTreeNode) {
  function ensureComponentsMapChildren (line 343) | function ensureComponentsMapChildren(id: string, children: ComponentTree...
  function updateComponentsMapData (line 351) | function updateComponentsMapData(data: ComponentTreeNode) {
  function addToComponentsMap (line 367) | function addToComponentsMap(data: ComponentTreeNode) {
  function loadComponent (line 375) | async function loadComponent(id: ComponentTreeNode['id']) {
  function sortChildren (line 386) | function sortChildren(children: ComponentTreeNode[]) {
  function compareIndexLists (line 404) | function compareIndexLists(a: number[], b: number[]): number {
  function getAppIdFromComponentId (line 416) | function getAppIdFromComponentId(id: string) {
  type ComponentUpdateTrackingEvent (line 422) | interface ComponentUpdateTrackingEvent {
  function addUpdateTrackingEvent (line 428) | function addUpdateTrackingEvent(instanceId: string, time: number) {

FILE: packages/app-frontend/src/features/components/composable/highlight.ts
  function useComponentHighlight (line 15) | function useComponentHighlight(id: Ref<string>) {

FILE: packages/app-frontend/src/features/components/composable/pick.ts
  function useComponentPick (line 6) | function useComponentPick() {

FILE: packages/app-frontend/src/features/components/composable/setup.ts
  function setupComponentsBridgeEvents (line 20) | function setupComponentsBridgeEvents(bridge: Bridge) {

FILE: packages/app-frontend/src/features/connection/index.ts
  function useAppConnection (line 10) | function useAppConnection() {
  function setAppConnected (line 34) | function setAppConnected(value: boolean, force = false, fromReload = fal...
  function setAppInitializing (line 54) | function setAppInitializing(value: boolean) {

FILE: packages/app-frontend/src/features/error/index.ts
  type ErrorMessage (line 3) | interface ErrorMessage {
  function putError (line 10) | function putError(message: string, icon: string = null) {
  function clearError (line 22) | function clearError() {
  function useError (line 26) | function useError() {

FILE: packages/app-frontend/src/features/header/tabs.ts
  function useTabs (line 4) | function useTabs() {

FILE: packages/app-frontend/src/features/inspector/custom/composable.ts
  type InspectorFromBackend (line 8) | interface InspectorFromBackend {
  type Inspector (line 27) | interface Inspector extends InspectorFromBackend {
  constant SELECTED_NODES_STORAGE (line 37) | const SELECTED_NODES_STORAGE = 'custom-inspector-selected-nodes'
  function inspectorFactory (line 40) | function inspectorFactory(options: InspectorFromBackend): Inspector {
  function useInspectors (line 55) | function useInspectors() {
  function useCurrentInspector (line 64) | function useCurrentInspector() {
  function fetchInspectors (line 133) | function fetchInspectors() {
  function fetchTree (line 137) | function fetchTree(inspector: Inspector) {
  function fetchState (line 148) | function fetchState(inspector: Inspector) {
  function resetInspectors (line 159) | function resetInspectors() {
  function setupCustomInspectorBridgeEvents (line 164) | function setupCustomInspectorBridgeEvents(bridge: Bridge) {

FILE: packages/app-frontend/src/features/layout/orientation.ts
  function useOrientation (line 5) | function useOrientation() {
  function switchOrientation (line 15) | function switchOrientation(mediaQueryEvent: MediaQueryListEvent | MediaQ...

FILE: packages/app-frontend/src/features/plugin/index.ts
  type Plugin (line 8) | interface Plugin {
  type PluginsPerApp (line 19) | interface PluginsPerApp {
  function getPlugins (line 25) | function getPlugins(appId: string) {
  function fetchPlugins (line 36) | function fetchPlugins() {
  function usePlugins (line 40) | function usePlugins() {
  function useComponentStateTypePlugin (line 50) | function useComponentStateTypePlugin() {
  function addPlugin (line 62) | function addPlugin(plugin: Plugin) {
  function setupPluginsBridgeEvents (line 73) | function setupPluginsBridgeEvents(bridge: Bridge) {

FILE: packages/app-frontend/src/features/timeline/composable/events.ts
  constant AUTOSCROLL_DURATION (line 27) | const AUTOSCROLL_DURATION = 10_000_000
  type AddEventCb (line 29) | type AddEventCb = (event: TimelineEvent) => void
  function onEventAdd (line 33) | function onEventAdd(cb: AddEventCb) {
  function addEvent (line 44) | function addEvent(appId: string, eventOptions: TimelineEvent, layer: Lay...
  function useSelectedEvent (line 124) | function useSelectedEvent() {
  function useInspectedEvent (line 131) | function useInspectedEvent() {
  function loadEvent (line 149) | function loadEvent(id: TimelineEvent['id']) {
  function selectEvent (line 157) | function selectEvent(event: TimelineEvent) {

FILE: packages/app-frontend/src/features/timeline/composable/layers.ts
  function layerFactory (line 22) | function layerFactory(options: LayerFromBackend): Layer {
  function getLayers (line 41) | function getLayers(appId: string) {
  function getHiddenLayers (line 50) | function getHiddenLayers(appId: string) {
  function useLayers (line 59) | function useLayers() {
  function fetchLayers (line 135) | async function fetchLayers() {
  function getGroupsAroundPosition (line 140) | function getGroupsAroundPosition(layer: Layer, startPosition: number, en...
  function addGroupAroundPosition (line 156) | function addGroupAroundPosition(layer: Layer, group: EventGroup, newPosi...

FILE: packages/app-frontend/src/features/timeline/composable/markers.ts
  function useMarkers (line 7) | function useMarkers() {
  function loadMarkers (line 22) | function loadMarkers() {

FILE: packages/app-frontend/src/features/timeline/composable/reset.ts
  type ResetCb (line 23) | type ResetCb = () => void
  function resetTimeline (line 27) | function resetTimeline(sync = true) {
  function resetTime (line 54) | function resetTime() {
  function onTimelineReset (line 60) | function onTimelineReset(cb: ResetCb) {

FILE: packages/app-frontend/src/features/timeline/composable/screenshot.ts
  function takeScreenshot (line 9) | async function takeScreenshot(event: TimelineEvent) {
  function useScreenshots (line 76) | function useScreenshots() {

FILE: packages/app-frontend/src/features/timeline/composable/setup.ts
  function setupTimelineBridgeEvents (line 22) | function setupTimelineBridgeEvents(bridge: Bridge) {

FILE: packages/app-frontend/src/features/timeline/composable/store.ts
  type TimelineEventFromBackend (line 6) | interface TimelineEventFromBackend {
  type EventGroup (line 15) | interface EventGroup {
  type EventScreenshot (line 27) | interface EventScreenshot {
  type TimelineEvent (line 34) | interface TimelineEvent extends TimelineEventFromBackend {
  type LayerFromBackend (line 47) | interface LayerFromBackend {
  type Layer (line 58) | interface Layer extends LayerFromBackend {
  type MarkerFromBackend (line 70) | interface MarkerFromBackend {
  type TimelineMarker (line 79) | interface TimelineMarker extends MarkerFromBackend {

FILE: packages/app-frontend/src/features/timeline/composable/time.ts
  function useTime (line 9) | function useTime() {
  function useCursor (line 18) | function useCursor() {

FILE: packages/app-frontend/src/features/ui/components/icons.ts
  method install (line 8) | install() {
  function generateHtmlIcon (line 33) | function generateHtmlIcon(icon: string) {

FILE: packages/app-frontend/src/features/ui/composables/useDisableScroll.ts
  function getScrollingElements (line 5) | function getScrollingElements() {
  function updateScroll (line 9) | function updateScroll() {
  function useDisableScroll (line 20) | function useDisableScroll() {

FILE: packages/app-frontend/src/features/ui/composables/useDisabled.ts
  function useDisabledParent (line 8) | function useDisabledParent(props: { disabled?: boolean }) {
  function useDisabledChild (line 27) | function useDisabledChild(props: { disabled?: boolean }) {

FILE: packages/app-frontend/src/features/ui/index.ts
  method install (line 24) | install(app) {

FILE: packages/app-frontend/src/index.ts
  function initDevTools (line 16) | async function initDevTools(shell: Shell) {

FILE: packages/app-frontend/src/mixins/data-field-edit.js
  function numberQuickEditMod (line 9) | function numberQuickEditMod(event) {
  method data (line 45) | data() {
  method cssClass (line 56) | cssClass() {
  method isEditable (line 62) | isEditable() {
  method isValueEditable (line 75) | isValueEditable() {
  method customField (line 91) | customField() {
  method inputType (line 95) | inputType() {
  method isSubfieldsEditable (line 102) | isSubfieldsEditable() {
  method valueValid (line 106) | valueValid() {
  method duplicateKey (line 119) | duplicateKey() {
  method keyValid (line 123) | keyValid() {
  method editValid (line 127) | editValid() {
  method quickEdits (line 131) | quickEdits() {
  method openEdit (line 165) | openEdit(focusKey = false) {
  method cancelEdit (line 198) | cancelEdit() {
  method submitEdit (line 204) | submitEdit() {
  method sendEdit (line 223) | sendEdit(payload) {
  method transformSpecialTokens (line 227) | transformSpecialTokens(str, display) {
  method quickEdit (line 247) | quickEdit(info, event) {
  method removeField (line 258) | removeField() {
  method addNewValue (line 262) | addNewValue() {
  method containsEdition (line 282) | containsEdition() {
  method cancelCurrentEdition (line 286) | cancelCurrentEdition() {
  method quickEditNumberTooltip (line 290) | quickEditNumberTooltip(operator) {

FILE: packages/app-frontend/src/mixins/entry-list.ts
  method mounted (line 17) | mounted() {
  method activated (line 21) | activated() {
  method refreshScrollToInspected (line 26) | refreshScrollToInspected() {
  function waitForFrame (line 58) | function waitForFrame() {

FILE: packages/app-frontend/src/mixins/keyboard.ts
  constant LEFT (line 3) | const LEFT = 'ArrowLeft'
  constant RIGHT (line 5) | const RIGHT = 'ArrowRight'
  constant DOWN (line 6) | const DOWN = 'ArrowDown'
  constant ENTER (line 7) | const ENTER = 'Enter'
  constant DEL (line 8) | const DEL = 'Delete'
  constant BACKSPACE (line 9) | const BACKSPACE = 'Backspace'
  function processEvent (line 13) | function processEvent(event, type) {
  method mounted (line 55) | mounted() {
  method unmounted (line 61) | unmounted() {

FILE: packages/app-frontend/src/plugins/global-refs.ts
  type Options (line 3) | interface Options {
  method install (line 8) | install(app: App, options: Options) {

FILE: packages/app-frontend/src/plugins/i18n.ts
  type StringMap (line 6) | interface StringMap { [key: string]: string | StringMap }
  type ValuesMap (line 7) | interface ValuesMap { [key: string]: any }
  type Replacer (line 8) | type Replacer = (text: string) => string
  function translate (line 14) | function translate(path: string | string[], values: ValuesMap = {}) {
  type Options (line 25) | interface Options {
  method install (line 32) | install(app, options: Options) {

FILE: packages/app-frontend/src/plugins/index.ts
  function setupPlugins (line 14) | function setupPlugins(app: App) {

FILE: packages/app-frontend/src/plugins/responsive.ts
  type Responsive (line 4) | interface Responsive {
  method install (line 14) | install(app) {

FILE: packages/app-frontend/src/router.ts
  constant STORAGE_ROUTE (line 91) | const STORAGE_ROUTE = 'route'
  function createRouterInstance (line 93) | function createRouterInstance() {

FILE: packages/app-frontend/src/types/vue.d.ts
  type ComponentCustomProperties (line 11) | interface ComponentCustomProperties {

FILE: packages/app-frontend/src/util/color.ts
  function toStrHex (line 3) | function toStrHex(color: number) {
  function dimColor (line 7) | function dimColor(color: number, dark: boolean, amount = 20) {
  function boostColor (line 18) | function boostColor(color: number, dark: boolean, amount = 10) {

FILE: packages/app-frontend/src/util/defer.ts
  function useDefer (line 3) | function useDefer(count = 10) {

FILE: packages/app-frontend/src/util/fonts.ts
  function installFonts (line 6) | async function installFonts() {
  function useFonts (line 37) | function useFonts() {

FILE: packages/app-frontend/src/util/format/time.ts
  type TimeFormat (line 1) | type TimeFormat = 'ms' | 'default'
  function formatTime (line 3) | function formatTime(timestamp: string | number | Date, format?: TimeForm...

FILE: packages/app-frontend/src/util/format/value.ts
  function valueType (line 14) | function valueType(value, raw = true) {
  function formattedValue (line 57) | function formattedValue(value, quotes = true) {
  function valueDetails (line 95) | function valueDetails(value: string) {

FILE: packages/app-frontend/src/util/keyboard.ts
  type KeyboardHandler (line 3) | type KeyboardHandler = (event: KeyboardEvent) => boolean | void | Promis...
  function handleKeyboard (line 5) | function handleKeyboard(type: 'keyup' | 'keydown', cb: KeyboardHandler, ...
  function onKeyUp (line 31) | function onKeyUp(cb: KeyboardHandler, force = false) {
  function onKeyDown (line 35) | function onKeyDown(cb: KeyboardHandler, force = false) {

FILE: packages/app-frontend/src/util/queue.ts
  class Queue (line 1) | class Queue<T = any> {
    method add (line 6) | add(value: T) {
    method shift (line 23) | shift(): T | null {
    method isEmpty (line 36) | isEmpty() {
    method has (line 40) | has(value: T) {
  type QueueItem (line 45) | interface QueueItem<T> {

FILE: packages/app-frontend/src/util/reactivity.ts
  function nonReactive (line 5) | function nonReactive<T>(ref: Ref<T>) {
  function addNonReactiveProperties (line 19) | function addNonReactiveProperties<T = any>(target: T, props: Partial<T>) {
  function useSavedRef (line 30) | function useSavedRef<T>(ref: Ref<T>, storageKey: string) {

FILE: packages/app-frontend/src/util/shared-data.ts
  function onSharedDataChange (line 4) | function onSharedDataChange(prop, handler) {

FILE: packages/app-frontend/src/util/theme.ts
  function useDarkMode (line 5) | function useDarkMode() {

FILE: packages/app-frontend/src/util/time.ts
  function useTimeAgo (line 10) | function useTimeAgo(time: Ref<number>) {

FILE: packages/shared-utils/src/backend.ts
  function getInstanceMap (line 8) | function getInstanceMap() {
  function getCustomInstanceDetails (line 12) | function getCustomInstanceDetails(instance) {
  function getCustomObjectDetails (line 16) | function getCustomObjectDetails(value, proto: string) {
  function isVueInstance (line 20) | function isVueInstance(value) {
  function getCustomRouterDetails (line 25) | function getCustomRouterDetails(router) {
  function getCustomStoreDetails (line 42) | function getCustomStoreDetails(store) {
  function getCatchedGetters (line 59) | function getCatchedGetters(store) {

FILE: packages/shared-utils/src/bridge.ts
  constant BATCH_DURATION (line 5) | const BATCH_DURATION = 100
  class Bridge (line 7) | class Bridge extends EventEmitter {
    method constructor (line 15) | constructor(wall) {
    method on (line 33) | on(event: string | symbol, listener: (...args: any[]) => void): this {
    method send (line 46) | send(event: string, payload?: any) {
    method log (line 61) | log(message: string) {
    method _flush (line 65) | _flush() {
    method _emit (line 75) | _emit(message) {
    method _send (line 92) | _send(messages) {
    method _nextSend (line 97) | _nextSend() {

FILE: packages/shared-utils/src/consts.ts
  type BuiltinTabs (line 1) | enum BuiltinTabs {
  type BridgeEvents (line 8) | enum BridgeEvents {
  type BridgeSubscriptions (line 98) | enum BridgeSubscriptions {
  type HookEvents (line 103) | enum HookEvents {

FILE: packages/shared-utils/src/edit.ts
  class StateEditor (line 3) | class StateEditor {
    method set (line 4) | set(object, path, value, cb = null) {
    method get (line 24) | get(object, path) {
    method has (line 38) | has(object, path, parent = false) {
    method createDefaultSetCallback (line 54) | createDefaultSetCallback(state: EditStatePayload) {
    method isRef (line 76) | isRef(_ref: any): boolean {
    method setRefValue (line 81) | setRefValue(_ref: any, _value: any): void {
    method getRefValue (line 85) | getRefValue(ref: any): any {

FILE: packages/shared-utils/src/env.ts
  function initEnv (line 23) | function initEnv(app: App) {

FILE: packages/shared-utils/src/plugin-permissions.ts
  type PluginPermission (line 3) | enum PluginPermission {
  function hasPluginPermission (line 10) | function hasPluginPermission(pluginId: string, permission: PluginPermiss...
  function setPluginPermission (line 18) | function setPluginPermission(pluginId: string, permission: PluginPermiss...

FILE: packages/shared-utils/src/plugin-settings.ts
  function getPluginSettings (line 4) | function getPluginSettings<TSettings extends Record<string, any> = any>(...
  function setPluginSettings (line 11) | function setPluginSettings<TSettings extends Record<string, any> = any>(...
  function getPluginDefaultSettings (line 18) | function getPluginDefaultSettings<TSettings extends Record<string, any> ...

FILE: packages/shared-utils/src/shared-data.ts
  type TSharedData (line 39) | type TSharedData = typeof internalSharedData
  type SharedDataParams (line 79) | interface SharedDataParams {
  function initSharedData (line 86) | function initSharedData(params: SharedDataParams): Promise<void> {
  function onSharedDataInit (line 176) | function onSharedDataInit(cb) {
  function destroySharedData (line 188) | function destroySharedData() {
  function setValue (line 193) | function setValue(key: string, value: any) {
  function sendValue (line 208) | function sendValue(key: string, value: any) {
  function watchSharedData (line 215) | function watchSharedData<

FILE: packages/shared-utils/src/shell.ts
  type Shell (line 3) | interface Shell {

FILE: packages/shared-utils/src/storage.ts
  function initStorage (line 10) | function initStorage(): Promise<void> {
  function getStorage (line 25) | function getStorage(key: string, defaultValue: any = null) {
  function setStorage (line 38) | function setStorage(key: string, val: any) {
  function removeStorage (line 52) | function removeStorage(key: string) {
  function clearStorage (line 66) | function clearStorage() {
  function checkStorage (line 80) | function checkStorage() {
  function getDefaultValue (line 86) | function getDefaultValue(value, defaultValue) {

FILE: packages/shared-utils/src/throttle.ts
  type ThrottleQueueItem (line 3) | interface ThrottleQueueItem {
  function createThrottleQueue (line 8) | function createThrottleQueue(wait: number) {

FILE: packages/shared-utils/src/transfer.ts
  constant MAX_SERIALIZED_SIZE (line 1) | const MAX_SERIALIZED_SIZE = 512 * 1024 // 1MB
  function encode (line 3) | function encode(data, replacer, list, seen) {
  function decode (line 53) | function decode(list, reviver) {
  function stringifyCircularAutoChunks (line 82) | function stringifyCircularAutoChunks(data: any, replacer: (this: any, ke...
  function parseCircularAutoChunks (line 103) | function parseCircularAutoChunks(data: any, reviver: (this: any, key: st...
  function stringifyStrictCircularAutoChunks (line 120) | function stringifyStrictCircularAutoChunks(data: any, replacer: (this: a...

FILE: packages/shared-utils/src/util.ts
  function cached (line 16) | function cached(fn) {
  function toUpper (line 47) | function toUpper(_, c) {
  function getComponentDisplayName (line 51) | function getComponentDisplayName(originalName, style = 'class') {
  function inDoc (line 63) | function inDoc(node) {
  constant UNDEFINED (line 78) | const UNDEFINED = '__vue_devtool_undefined__'
  constant INFINITY (line 79) | const INFINITY = '__vue_devtool_infinity__'
  constant NEGATIVE_INFINITY (line 80) | const NEGATIVE_INFINITY = '__vue_devtool_negative_infinity__'
  constant NAN (line 81) | const NAN = '__vue_devtool_nan__'
  constant SPECIAL_TOKENS (line 83) | const SPECIAL_TOKENS = {
  constant MAX_STRING_SIZE (line 93) | const MAX_STRING_SIZE = 10000
  constant MAX_ARRAY_SIZE (line 94) | const MAX_ARRAY_SIZE = 5000
  function specialTokenToString (line 96) | function specialTokenToString(value) {
  class EncodeCache (line 122) | class EncodeCache {
    method constructor (line 125) | constructor() {
    method cache (line 134) | cache<TResult, TData>(data: TData, factory: (data: TData) => TResult):...
    method clear (line 146) | clear() {
  class ReviveCache (line 153) | class ReviveCache {
    method constructor (line 159) | constructor(maxSize: number) {
    method cache (line 166) | cache(value: any) {
    method read (line 178) | read(id: number) {
  function stringify (line 190) | function stringify(data, target: keyof typeof replacers = 'internal') {
  function replacerForInternal (line 196) | function replacerForInternal(key) {
  function replaceForUser (line 291) | function replaceForUser(key) {
  function getCustomMapDetails (line 316) | function getCustomMapDetails(val) {
  function reviveMap (line 337) | function reviveMap(val) {
  function getCustomSetDetails (line 347) | function getCustomSetDetails(val) {
  function reviveSet (line 359) | function reviveSet(val) {
  function getCustomBigIntDetails (line 369) | function getCustomBigIntDetails(val) {
  function getCustomDateDetails (line 380) | function getCustomDateDetails(val: Date) {
  function basename (line 397) | function basename(filename, ext) {
  function getComponentName (line 408) | function getComponentName(options) {
  function getCustomComponentDefinitionDetails (line 419) | function getCustomComponentDefinitionDetails(def) {
  function getCustomFunctionDetails (line 443) | function getCustomFunctionDetails(func: Function): CustomState {
  function getCustomHTMLElementDetails (line 469) | function getCustomHTMLElementDetails(value: HTMLElement): CustomState {
  function namedNodeMapToObject (line 499) | function namedNodeMapToObject(map: NamedNodeMap) {
  function getCustomRefDetails (line 509) | function getCustomRefDetails(instance, key, ref) {
  function parse (line 541) | function parse(data: any, revive = false) {
  function reviver (line 550) | function reviver(key, val) {
  function revive (line 554) | function revive(val) {
  function sanitize (line 617) | function sanitize(data) {
  function isPlainObject (line 632) | function isPlainObject(obj) {
  function isPrimitive (line 636) | function isPrimitive(data) {
  function searchDeepInObject (line 654) | function searchDeepInObject(obj, searchTerm) {
  constant SEARCH_MAX_DEPTH (line 661) | const SEARCH_MAX_DEPTH = 10
  function internalSearchObject (line 671) | function internalSearchObject(obj, searchTerm, seen, depth) {
  function internalSearchArray (line 697) | function internalSearchArray(array, searchTerm, seen, depth) {
  function internalSearchCheck (line 722) | function internalSearchCheck(searchTerm, key, value, seen, depth) {
  function compare (line 760) | function compare(value, searchTerm) {
  function sortByKey (line 764) | function sortByKey(state) {
  function simpleGet (line 776) | function simpleGet(object, path) {
  function focusInput (line 787) | function focusInput(el) {
  function openInEditor (line 792) | function openInEditor(file) {
  constant ESC (line 818) | const ESC = {
  function escape (line 825) | function escape(s) {
  function escapeChar (line 829) | function escapeChar(a) {
  function copyToClipboard (line 833) | function copyToClipboard(state) {
  function isEmptyObject (line 855) | function isEmptyObject(obj) {
  function chunk (line 865) | function chunk(array: unknown[], size: number): unknown[][] {

FILE: packages/shell-chrome/src/backend.js
  function sendListening (line 8) | function sendListening() {
  function handshake (line 16) | function handshake(e) {

FILE: packages/shell-chrome/src/detector-exec.js
  function sendMessage (line 3) | function sendMessage(message) {
  function detect (line 10) | function detect() {

FILE: packages/shell-chrome/src/devtools-background.js
  function createPanelIfHasVue (line 13) | function createPanelIfHasVue() {
  function onPanelShown (line 42) | function onPanelShown() {
  function onPanelHidden (line 46) | function onPanelHidden() {

FILE: packages/shell-chrome/src/devtools.js
  method connect (line 18) | connect(cb) {
  method onReload (line 87) | onReload(reloadFn) {
  function injectScript (line 100) | function injectScript(scriptName, cb) {

FILE: packages/shell-chrome/src/proxy.js
  function sendMessageToBackend (line 16) | function sendMessageToBackend(payload) {
  function sendMessageToDevtools (line 23) | function sendMessageToDevtools(e) {
  function handleDisconnect (line 34) | function handleDisconnect() {

FILE: packages/shell-chrome/src/service-worker.js
  function isNumeric (line 32) | function isNumeric(str) {
  function installProxy (line 36) | function installProxy(tabId) {
  function doublePipe (line 53) | function doublePipe(id, one, two) {

FILE: packages/shell-dev-vue2/src/MyClass.js
  class MyClass (line 1) | class MyClass {
    method constructor (line 2) | constructor() {

FILE: packages/shell-dev-vue2/src/dynamic-module.js
  method state (line 3) | state() {
  method state (line 24) | state() {
  method state (line 48) | state() {

FILE: packages/shell-dev-vue2/src/index.js
  method render (line 47) | render(h) {
  method render (line 71) | render(h) {
  method render (line 87) | render(h) {

FILE: packages/shell-dev-vue2/src/store.js
  method state (line 64) | state() {
  method state (line 86) | state() {
  method state (line 101) | state() {
  method state (line 111) | state() {
  function wait (line 123) | function wait(ms) {

FILE: packages/shell-dev-vue2/webpack.config.js
  method onBeforeSetupMiddleware (line 37) | onBeforeSetupMiddleware({ app }) {

FILE: packages/shell-dev-vue3/src/SetupRender.js
  method setup (line 6) | setup() {

FILE: packages/shell-dev-vue3/src/store.js
  method state (line 4) | state() {
  method increment (line 17) | increment(state) {
  method state (line 23) | state() {
  method state (line 34) | state() {
  method state (line 46) | state() {

FILE: packages/shell-dev-vue3/webpack.config.js
  method onBeforeSetupMiddleware (line 23) | onBeforeSetupMiddleware({ app }) {

FILE: packages/shell-electron/app.js
  function createWindow (line 10) | function createWindow() {

FILE: packages/shell-electron/index.js
  method connect (line 17) | connect(host, port, { io, showToast, app } = {}) {

FILE: packages/shell-electron/src/backend.js
  constant MAX_DATA_CHUNK (line 11) | const MAX_DATA_CHUNK = 2000
  function connectedMessage (line 13) | function connectedMessage() {
  function disconnectedMessage (line 19) | function disconnectedMessage() {
  method listen (line 46) | listen(fn) {
  method send (line 49) | send(data) {

FILE: packages/shell-electron/src/devtools.js
  method connect (line 32) | connect(callback) {
  method onReload (line 49) | onReload(fn) {

FILE: packages/shell-firefox/src/backend.js
  function sendListening (line 8) | function sendListening() {
  function handshake (line 16) | function handshake(e) {

FILE: packages/shell-firefox/src/background.js
  function isNumeric (line 32) | function isNumeric(str) {
  function installProxy (line 36) | function installProxy(tabId) {
  function doublePipe (line 52) | function doublePipe(id, one, two) {

FILE: packages/shell-firefox/src/detector.js
  function detect (line 10) | function detect(win) {
  function installScript (line 87) | function installScript(fn) {

FILE: packages/shell-firefox/src/devtools-background.js
  function createPanelIfHasVue (line 13) | function createPanelIfHasVue() {
  function onPanelShown (line 42) | function onPanelShown() {
  function onPanelHidden (line 46) | function onPanelHidden() {

FILE: packages/shell-firefox/src/devtools.js
  method connect (line 18) | connect(cb) {
  method onReload (line 87) | onReload(reloadFn) {
  function injectScript (line 100) | function injectScript(scriptName, cb) {

FILE: packages/shell-firefox/src/proxy.js
  function sendMessageToBackend (line 16) | function sendMessageToBackend(payload) {
  function sendMessageToDevtools (line 23) | function sendMessageToDevtools(e) {
  function handleDisconnect (line 34) | function handleDisconnect() {

FILE: packages/shell-host/src/backend.js
  method listen (line 5) | listen(fn) {
  method send (line 8) | send(data) {

FILE: packages/shell-host/webpack.config.js
  method onBeforeSetupMiddleware (line 16) | onBeforeSetupMiddleware({ app }) {

FILE: release.js
  constant IS_CI (line 8) | const IS_CI = !!(process.env.CIRCLECI || process.env.GITHUB_ACTIONS)
  function applyIcons (line 78) | function applyIcons(manifest, suffix = '') {
Condensed preview — 404 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,008K chars).
[
  {
    "path": ".browserslistrc",
    "chars": 27,
    "preview": "Chrome >= 52\nFirefox >= 48\n"
  },
  {
    "path": ".circleci/config.yml",
    "chars": 866,
    "preview": "version: 2\njobs:\n  build:\n    docker:\n      # specify the version you desire here\n      - image: node:current\n      - im"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 15,
    "preview": "github: Akryum\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 3380,
    "preview": "name: 🐞 Bug report\ndescription: Create a report to help us improve\nbody:\n  - type: markdown\n    attributes:\n      value:"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 669,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: I have a performance issue\n    url: https://devtools.vuejs.org/guid"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1142,
    "preview": "name: 🚀 New feature proposal\ndescription: Suggest an idea for this project\nlabels: [':sparkles: feature request']\nbody:\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1162,
    "preview": "<!-- Thank you for contributing! -->\n\n### Description\n\n<!-- Please insert your description here and provide especially i"
  },
  {
    "path": ".github/commit-convention.md",
    "chars": 2902,
    "preview": "## Git Commit Message Convention\n\n> This is adapted from [Angular's commit convention](https://github.com/conventional-c"
  },
  {
    "path": ".github/workflows/create-release.yml",
    "chars": 477,
    "preview": "name: Create release\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  build:\n    name: Create Release\n    runs-on: ubuntu-la"
  },
  {
    "path": ".gitignore",
    "chars": 171,
    "preview": "node_modules\n.DS_Store\nbuild\n/dist/*\n*.zip\n*.xpi\ntests_output\nselenium-debug.log\nTODOs.md\n.idea\n.web-extension-id\nyarn-e"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 55,
    "preview": "{\n  \"typescript.tsdk\": \"node_modules/typescript/lib\"\n}\n"
  },
  {
    "path": "LICENSE",
    "chars": 1083,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014-present Evan You\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "README.md",
    "chars": 1951,
    "preview": "# Try the next iteration of Vue Devtools!\n\nWe have a brand new version of Devtools being developed at [vuejs/devtools-ne"
  },
  {
    "path": "babel.config.js",
    "chars": 127,
    "preview": "module.exports = {\n  root: true,\n  presets: [\n    [\n      '@babel/env',\n      {\n        modules: false,\n      },\n    ],\n"
  },
  {
    "path": "cypress/.eslintrc.js",
    "chars": 151,
    "preview": "module.exports = {\n  plugins: [\n    'cypress',\n  ],\n  env: {\n    'mocha': true,\n    'cypress/globals': true,\n  },\n  rule"
  },
  {
    "path": "cypress/.gitignore",
    "chars": 21,
    "preview": "/screenshots\n/videos\n"
  },
  {
    "path": "cypress/fixtures/example.json",
    "chars": 155,
    "preview": "{\n  \"name\": \"Using fixtures to represent data\",\n  \"email\": \"hello@cypress.io\",\n  \"body\": \"Fixtures are a great way to mo"
  },
  {
    "path": "cypress/integration/component-data-edit.js",
    "chars": 4345,
    "preview": "import { suite } from '../utils/suite'\n\nsuite('component data edit', () => {\n  it('should edit data using the decrease b"
  },
  {
    "path": "cypress/integration/components-tab.js",
    "chars": 5297,
    "preview": "import { suite } from '../utils/suite'\n\nconst baseInstanceCount = 12\n\nsuite('components tab', () => {\n  beforeEach(() =>"
  },
  {
    "path": "cypress/integration/events-tab.js",
    "chars": 1383,
    "preview": "import { suite } from '../utils/suite'\n\nsuite('events tab', () => {\n  it('should display new events counter', () => {\n  "
  },
  {
    "path": "cypress/integration/vuex-edit.js",
    "chars": 3426,
    "preview": "import { suite } from '../utils/suite'\n\nsuite('vuex edit', () => {\n  it('should edit state using the decrease button', ("
  },
  {
    "path": "cypress/integration/vuex-tab.js",
    "chars": 8735,
    "preview": "import { suite } from '../utils/suite'\n\nsuite('vuex tab', () => {\n  it('should display mutations history', () => {\n    c"
  },
  {
    "path": "cypress/plugins/index.js",
    "chars": 838,
    "preview": "// ***********************************************************\n// This example plugins/index.js can be used to load plug"
  },
  {
    "path": "cypress/support/commands.js",
    "chars": 1668,
    "preview": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom"
  },
  {
    "path": "cypress/support/index.js",
    "chars": 670,
    "preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
  },
  {
    "path": "cypress/utils/suite.js",
    "chars": 165,
    "preview": "export function suite(description, tests) {\n  describe(description, () => {\n    before(() => {\n      cy.visit('/')\n     "
  },
  {
    "path": "cypress.json",
    "chars": 83,
    "preview": "{\n  \"viewportWidth\": 1280,\n  \"viewportHeight\": 800,\n  \"chromeWebSecurity\": false\n}\n"
  },
  {
    "path": "eslint.config.js",
    "chars": 879,
    "preview": "const antfu = require('@antfu/eslint-config').default\n\nmodule.exports = antfu({\n  ignores: [\n    '**/dist',\n  ],\n}, {\n  "
  },
  {
    "path": "extension-zips.js",
    "chars": 4287,
    "preview": "// require modules\nconst fs = require('node:fs')\nconst path = require('node:path')\nconst process = require('node:process"
  },
  {
    "path": "lerna.json",
    "chars": 158,
    "preview": "{\n  \"npmClient\": \"yarn\",\n  \"useWorkspaces\": true,\n  \"version\": \"6.0.0-beta.2\",\n  \"packages\": [\n    \"packages/*\"\n  ],\n  \""
  },
  {
    "path": "package.json",
    "chars": 3123,
    "preview": "{\n  \"name\": \"vue-devtools\",\n  \"version\": \"6.6.4\",\n  \"private\": true,\n  \"description\": \"devtools for Vue.js!\",\n  \"workspa"
  },
  {
    "path": "packages/api/package.json",
    "chars": 973,
    "preview": "{\n  \"name\": \"@vue/devtools-api\",\n  \"version\": \"6.6.4\",\n  \"description\": \"Interact with the Vue devtools from the page\",\n"
  },
  {
    "path": "packages/api/src/api/api.ts",
    "chars": 3309,
    "preview": "import type { ComponentBounds, Hookable } from './hooks.js'\nimport type { Context } from './context.js'\nimport type { Co"
  },
  {
    "path": "packages/api/src/api/app.ts",
    "chars": 31,
    "preview": "export type App = any // @TODO\n"
  },
  {
    "path": "packages/api/src/api/component.ts",
    "chars": 1956,
    "preview": "import type { InspectorNodeTag } from './api.js'\nimport type { ID } from './util.js'\n\nexport type ComponentInstance = an"
  },
  {
    "path": "packages/api/src/api/context.ts",
    "chars": 123,
    "preview": "import type { AppRecord } from './api.js'\n\nexport interface Context {\n  currentTab: string\n  currentAppRecord: AppRecord"
  },
  {
    "path": "packages/api/src/api/hooks.ts",
    "chars": 6905,
    "preview": "import type { ComponentDevtoolsOptions, ComponentInstance, ComponentTreeNode, InspectedComponentData } from './component"
  },
  {
    "path": "packages/api/src/api/index.ts",
    "chars": 163,
    "preview": "export * from './api.js'\nexport * from './app.js'\nexport * from './component.js'\nexport * from './context.js'\nexport * f"
  },
  {
    "path": "packages/api/src/api/util.ts",
    "chars": 71,
    "preview": "export type ID = number | string\n\nexport interface WithId {\n  id: ID\n}\n"
  },
  {
    "path": "packages/api/src/const.ts",
    "chars": 112,
    "preview": "export const HOOK_SETUP = 'devtools-plugin:setup'\nexport const HOOK_PLUGIN_SETTINGS_SET = 'plugin:settings:set'\n"
  },
  {
    "path": "packages/api/src/env.ts",
    "chars": 812,
    "preview": "import type { ApiProxy } from './proxy.js'\nimport type { PluginDescriptor, SetupFunction } from './index.js'\n\nexport int"
  },
  {
    "path": "packages/api/src/index.ts",
    "chars": 2081,
    "preview": "import { getDevtoolsGlobalHook, getTarget, isProxyAvailable } from './env.js'\nimport { HOOK_SETUP } from './const.js'\nim"
  },
  {
    "path": "packages/api/src/plugin.ts",
    "chars": 1205,
    "preview": "import type { App } from './api/index.js'\n\nexport interface PluginDescriptor {\n  id: string\n  label: string\n  app: App\n "
  },
  {
    "path": "packages/api/src/proxy.ts",
    "chars": 3337,
    "preview": "import type { Context, DevtoolsPluginApi, Hookable } from './api/index.js'\nimport type { PluginDescriptor } from './plug"
  },
  {
    "path": "packages/api/src/time.ts",
    "chars": 580,
    "preview": "let supported: boolean\nlet perf: Performance\n\nexport function isPerformanceSupported() {\n  if (supported !== undefined) "
  },
  {
    "path": "packages/api/tsconfig.json",
    "chars": 612,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"module\": \"ESNext\",\n    \"moduleResolu"
  },
  {
    "path": "packages/app-backend-api/package.json",
    "chars": 502,
    "preview": "{\n  \"name\": \"@vue-devtools/app-backend-api\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"./lib/index.js\",\n  \"typ"
  },
  {
    "path": "packages/app-backend-api/src/api.ts",
    "chars": 11061,
    "preview": "import type {\n  Bridge,\n} from '@vue-devtools/shared-utils'\nimport {\n  HookEvents,\n  PluginPermission,\n  StateEditor,\n  "
  },
  {
    "path": "packages/app-backend-api/src/app-record.ts",
    "chars": 756,
    "preview": "import type { App, ComponentInstance } from '@vue/devtools-api'\nimport type { DevtoolsBackend } from './backend'\n\nexport"
  },
  {
    "path": "packages/app-backend-api/src/backend-context.ts",
    "chars": 1876,
    "preview": "import type { Bridge } from '@vue-devtools/shared-utils'\nimport type {\n  CustomInspectorOptions,\n  ID,\n  TimelineEventOp"
  },
  {
    "path": "packages/app-backend-api/src/backend.ts",
    "chars": 882,
    "preview": "import type { AppRecord } from './app-record'\nimport { DevtoolsApi } from './api'\nimport type { BackendContext } from '."
  },
  {
    "path": "packages/app-backend-api/src/global-hook.ts",
    "chars": 369,
    "preview": "import type { AppRecordOptions } from './app-record'\n\nexport interface DevtoolsHook {\n  emit: (event: string, ...payload"
  },
  {
    "path": "packages/app-backend-api/src/hooks.ts",
    "chars": 6182,
    "preview": "import { PluginPermission, SharedData, hasPluginPermission } from '@vue-devtools/shared-utils'\nimport type { HookHandler"
  },
  {
    "path": "packages/app-backend-api/src/index.ts",
    "chars": 190,
    "preview": "export * from './api'\nexport * from './app-record'\nexport * from './backend'\nexport * from './backend-context'\nexport * "
  },
  {
    "path": "packages/app-backend-api/src/plugin.ts",
    "chars": 173,
    "preview": "import type { PluginDescriptor, SetupFunction } from '@vue/devtools-api'\n\nexport interface Plugin {\n  descriptor: Plugin"
  },
  {
    "path": "packages/app-backend-api/tsconfig.json",
    "chars": 588,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2019\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"resolveJ"
  },
  {
    "path": "packages/app-backend-core/package.json",
    "chars": 750,
    "preview": "{\n  \"name\": \"@vue-devtools/app-backend-core\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"./lib/index.js\",\n  \"ty"
  },
  {
    "path": "packages/app-backend-core/src/app.ts",
    "chars": 8584,
    "preview": "import type {\n  AppRecord,\n  AppRecordOptions,\n  BackendContext,\n  DevtoolsBackend,\n  SimpleAppRecord,\n} from '@vue-devt"
  },
  {
    "path": "packages/app-backend-core/src/backend.ts",
    "chars": 1049,
    "preview": "import type { BackendContext, DevtoolsBackend, DevtoolsBackendOptions } from '@vue-devtools/app-backend-api'\nimport { cr"
  },
  {
    "path": "packages/app-backend-core/src/component-pick.ts",
    "chars": 3456,
    "preview": "import { BridgeEvents, isBrowser } from '@vue-devtools/shared-utils'\nimport type { BackendContext, DevtoolsBackend } fro"
  },
  {
    "path": "packages/app-backend-core/src/component.ts",
    "chars": 5439,
    "preview": "import { BridgeEvents, SharedData, createThrottleQueue, parse, stringify } from '@vue-devtools/shared-utils'\nimport type"
  },
  {
    "path": "packages/app-backend-core/src/flash.ts",
    "chars": 1417,
    "preview": "import type { DevtoolsBackend } from '@vue-devtools/app-backend-api'\nimport type { ComponentInstance } from '@vue/devtoo"
  },
  {
    "path": "packages/app-backend-core/src/global-hook.ts",
    "chars": 245,
    "preview": "import type { DevtoolsHook } from '@vue-devtools/app-backend-api'\nimport { target } from '@vue-devtools/shared-utils'\n\n/"
  },
  {
    "path": "packages/app-backend-core/src/highlighter.ts",
    "chars": 5523,
    "preview": "import { isBrowser } from '@vue-devtools/shared-utils'\nimport type { BackendContext, DevtoolsBackend } from '@vue-devtoo"
  },
  {
    "path": "packages/app-backend-core/src/hook.ts",
    "chars": 19495,
    "preview": "// this script is injected into every page.\n\n/**\n * Install the hook on window, which is an event emitter.\n * Note becau"
  },
  {
    "path": "packages/app-backend-core/src/index.ts",
    "chars": 22877,
    "preview": "import type {\n  AppRecord,\n  BackendContext,\n  Plugin,\n} from '@vue-devtools/app-backend-api'\nimport {\n  BuiltinBackendF"
  },
  {
    "path": "packages/app-backend-core/src/inspector.ts",
    "chars": 2875,
    "preview": "import type { App } from '@vue/devtools-api'\nimport type { BackendContext, CustomInspector } from '@vue-devtools/app-bac"
  },
  {
    "path": "packages/app-backend-core/src/legacy/scan.ts",
    "chars": 2425,
    "preview": "import { isBrowser, target } from '@vue-devtools/shared-utils'\nimport { getPageConfig } from '../page-config'\n\nconst roo"
  },
  {
    "path": "packages/app-backend-core/src/page-config.ts",
    "chars": 649,
    "preview": "import { SharedData, target } from '@vue-devtools/shared-utils'\n\nexport interface PageConfig {\n  openInEditorHost?: stri"
  },
  {
    "path": "packages/app-backend-core/src/perf.ts",
    "chars": 5014,
    "preview": "import type { BackendContext, DevtoolsBackend } from '@vue-devtools/app-backend-api'\nimport type { App, ComponentInstanc"
  },
  {
    "path": "packages/app-backend-core/src/plugin.ts",
    "chars": 2628,
    "preview": "import type { PluginQueueItem } from '@vue/devtools-api'\nimport type { BackendContext, Plugin } from '@vue-devtools/app-"
  },
  {
    "path": "packages/app-backend-core/src/timeline-builtins.ts",
    "chars": 3367,
    "preview": "import type { TimelineLayerOptions } from '@vue/devtools-api'\n\nexport const builtinLayers: TimelineLayerOptions[] = [\n  "
  },
  {
    "path": "packages/app-backend-core/src/timeline-marker.ts",
    "chars": 1692,
    "preview": "import type { BackendContext, TimelineMarker } from '@vue-devtools/app-backend-api'\nimport { BridgeEvents, SharedData } "
  },
  {
    "path": "packages/app-backend-core/src/timeline-screenshot.ts",
    "chars": 3471,
    "preview": "import type { BackendContext } from '@vue-devtools/app-backend-api'\nimport type { ID, ScreenshotOverlayRenderContext } f"
  },
  {
    "path": "packages/app-backend-core/src/timeline.ts",
    "chars": 7032,
    "preview": "import type { AppRecord, BackendContext } from '@vue-devtools/app-backend-api'\nimport { BridgeEvents, HookEvents, Shared"
  },
  {
    "path": "packages/app-backend-core/src/toast.ts",
    "chars": 46,
    "preview": "export function installToast() {\n  // @TODO\n}\n"
  },
  {
    "path": "packages/app-backend-core/src/util/queue.ts",
    "chars": 817,
    "preview": "export interface Job {\n  id: string\n  fn: () => Promise<void>\n}\n\nexport class JobQueue {\n  jobs: Job[] = []\n  currentJob"
  },
  {
    "path": "packages/app-backend-core/src/util/subscriptions.ts",
    "chars": 516,
    "preview": "const activeSubs: Map<string, Map<string, boolean>> = new Map()\n\nfunction getSubs(type: string) {\n  let subs = activeSub"
  },
  {
    "path": "packages/app-backend-core/tsconfig.json",
    "chars": 588,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2019\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"resolveJ"
  },
  {
    "path": "packages/app-backend-vue1/package.json",
    "chars": 508,
    "preview": "{\n  \"name\": \"@vue-devtools/app-backend-vue1\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"./lib/index.js\",\n  \"ty"
  },
  {
    "path": "packages/app-backend-vue1/src/index.ts",
    "chars": 178,
    "preview": "import { defineBackend } from '@vue-devtools/app-backend-api'\n\nexport const backend = defineBackend({\n  frameworkVersion"
  },
  {
    "path": "packages/app-backend-vue1/tsconfig.json",
    "chars": 588,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2019\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"resolveJ"
  },
  {
    "path": "packages/app-backend-vue2/package.json",
    "chars": 681,
    "preview": "{\n  \"name\": \"@vue-devtools/app-backend-vue2\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"./lib/index.js\",\n  \"ty"
  },
  {
    "path": "packages/app-backend-vue2/src/components/data.ts",
    "chars": 12204,
    "preview": "import type { StateEditor } from '@vue-devtools/shared-utils'\nimport { SharedData, camelize, getComponentName, getCustom"
  },
  {
    "path": "packages/app-backend-vue2/src/components/el.ts",
    "chars": 3067,
    "preview": "import { inDoc, isBrowser, target } from '@vue-devtools/shared-utils'\n\nfunction createRect() {\n  const rect = {\n    top:"
  },
  {
    "path": "packages/app-backend-vue2/src/components/perf.ts",
    "chars": 1609,
    "preview": "import type { DevtoolsApi } from '@vue-devtools/app-backend-api'\nimport { HookEvents, SharedData } from '@vue-devtools/s"
  },
  {
    "path": "packages/app-backend-vue2/src/components/tree.ts",
    "chars": 11173,
    "preview": "import type { AppRecord, BackendContext, DevtoolsApi } from '@vue-devtools/app-backend-api'\nimport { classify, kebabize "
  },
  {
    "path": "packages/app-backend-vue2/src/components/update-tracking.ts",
    "chars": 1447,
    "preview": "import type { DevtoolsApi } from '@vue-devtools/app-backend-api'\nimport { HookEvents, SharedData } from '@vue-devtools/s"
  },
  {
    "path": "packages/app-backend-vue2/src/components/util.ts",
    "chars": 1277,
    "preview": "import { getComponentName } from '@vue-devtools/shared-utils'\nimport type { AppRecord } from '@vue-devtools/app-backend-"
  },
  {
    "path": "packages/app-backend-vue2/src/events.ts",
    "chars": 1120,
    "preview": "import type { BackendContext } from '@vue-devtools/app-backend-api'\nimport { HookEvents } from '@vue-devtools/shared-uti"
  },
  {
    "path": "packages/app-backend-vue2/src/index.ts",
    "chars": 3769,
    "preview": "import { BuiltinBackendFeature, defineBackend } from '@vue-devtools/app-backend-api'\nimport { backendInjections, getComp"
  },
  {
    "path": "packages/app-backend-vue2/src/plugin.ts",
    "chars": 14794,
    "preview": "import type { DevtoolsApi } from '@vue-devtools/app-backend-api'\nimport type { App, ComponentState, CustomInspectorNode,"
  },
  {
    "path": "packages/app-backend-vue2/tsconfig.json",
    "chars": 588,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2019\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"resolveJ"
  },
  {
    "path": "packages/app-backend-vue3/package.json",
    "chars": 550,
    "preview": "{\n  \"name\": \"@vue-devtools/app-backend-vue3\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"./lib/index.js\",\n  \"ty"
  },
  {
    "path": "packages/app-backend-vue3/src/components/data.ts",
    "chars": 13583,
    "preview": "import type { BackendContext } from '@vue-devtools/app-backend-api'\nimport type { StateEditor } from '@vue-devtools/shar"
  },
  {
    "path": "packages/app-backend-vue3/src/components/el.ts",
    "chars": 3575,
    "preview": "import { inDoc, isBrowser } from '@vue-devtools/shared-utils'\nimport { isFragment } from './util'\n\nexport function getCo"
  },
  {
    "path": "packages/app-backend-vue3/src/components/filter.ts",
    "chars": 533,
    "preview": "import { classify, kebabize } from '@vue-devtools/shared-utils'\nimport { getInstanceName } from './util'\n\nexport class C"
  },
  {
    "path": "packages/app-backend-vue3/src/components/tree.ts",
    "chars": 8385,
    "preview": "import type { BackendContext, DevtoolsApi } from '@vue-devtools/app-backend-api'\nimport type { ComponentTreeNode } from "
  },
  {
    "path": "packages/app-backend-vue3/src/components/util.ts",
    "chars": 2757,
    "preview": "import { basename, classify } from '@vue-devtools/shared-utils'\nimport type { App, ComponentInstance } from '@vue/devtoo"
  },
  {
    "path": "packages/app-backend-vue3/src/index.ts",
    "chars": 3621,
    "preview": "import { defineBackend } from '@vue-devtools/app-backend-api'\nimport { HookEvents, backendInjections } from '@vue-devtoo"
  },
  {
    "path": "packages/app-backend-vue3/src/util.ts",
    "chars": 324,
    "preview": "export function flatten(items) {\n  return items.reduce((acc, item) => {\n    if (Array.isArray(item)) {\n      acc.push(.."
  },
  {
    "path": "packages/app-backend-vue3/tsconfig.json",
    "chars": 588,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2019\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"resolveJ"
  },
  {
    "path": "packages/app-frontend/package.json",
    "chars": 903,
    "preview": "{\n  \"name\": \"@vue-devtools/app-frontend\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@pixi/events"
  },
  {
    "path": "packages/app-frontend/src/app.ts",
    "chars": 2585,
    "preview": "import type { App as VueApp } from 'vue'\nimport { createApp as createVueApp } from 'vue'\nimport { BridgeEvents, SharedDa"
  },
  {
    "path": "packages/app-frontend/src/assets/github-theme/dark.json",
    "chars": 15172,
    "preview": "{\n  \"inherit\": true,\n  \"base\": \"vs-dark\",\n  \"colors\": {\n    \"focusBorder\": \"#388bfd\",\n    \"foreground\": \"#c9d1d9\",\n    \""
  },
  {
    "path": "packages/app-frontend/src/assets/github-theme/light.json",
    "chars": 14959,
    "preview": "{\n  \"inherit\": true,\n  \"base\": \"vs\",\n  \"colors\": {\n    \"focusBorder\": \"#0366d6\",\n    \"foreground\": \"#24292e\",\n    \"descr"
  },
  {
    "path": "packages/app-frontend/src/assets/style/imports.styl",
    "chars": 57,
    "preview": "@import '~@vue/ui/src/style/imports'\n@import 'variables'\n"
  },
  {
    "path": "packages/app-frontend/src/assets/style/index.postcss",
    "chars": 2194,
    "preview": "html, body, #app {\n  @apply dark:!bg-gray-800;\n}\n\n.vue-ui-high-contrast {\n  #app {\n    @apply !bg-black;\n  }\n}\n\n/* Poppe"
  },
  {
    "path": "packages/app-frontend/src/assets/style/index.styl",
    "chars": 2170,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@import '~@vue/ui/dist/vue-ui.css'\n\n@import 'imports'\n@impor"
  },
  {
    "path": "packages/app-frontend/src/assets/style/transitions.styl",
    "chars": 372,
    "preview": ".slide-up-enter\n  opacity 0\n  transform translate(0, 50%)\n\n.slide-up-leave-to\n  opacity 0\n  transform translate(0, -50%)"
  },
  {
    "path": "packages/app-frontend/src/assets/style/variables.styl",
    "chars": 1739,
    "preview": "// Colors\n$blue = #44A1FF\n$grey = #DDDDDD\n$darkGrey = #CCC\n$darkerGrey = #AAA\n$blueishGrey = #486887\n$green = #42B983\n$d"
  },
  {
    "path": "packages/app-frontend/src/features/App.vue",
    "chars": 3674,
    "preview": "<script lang=\"ts\">\nimport { defineComponent, onMounted } from 'vue'\nimport {\n  SharedData,\n  getStorage,\n  isChrome,\n  o"
  },
  {
    "path": "packages/app-frontend/src/features/apps/AppSelect.vue",
    "chars": 3153,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, watch } from 'vue'\nimport { BridgeEvents, SharedData } from '@vue"
  },
  {
    "path": "packages/app-frontend/src/features/apps/AppSelectItem.vue",
    "chars": 1808,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent } from 'vue'\nimport { useVueVersionCheck } from './vue-version-che"
  },
  {
    "path": "packages/app-frontend/src/features/apps/AppSelectPane.vue",
    "chars": 2808,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport { BridgeEvents, SharedData } from "
  },
  {
    "path": "packages/app-frontend/src/features/apps/AppSelectPaneItem.vue",
    "chars": 2162,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent } from 'vue'\nimport { useVueVersionCheck } from './vue-version-che"
  },
  {
    "path": "packages/app-frontend/src/features/apps/index.ts",
    "chars": 2505,
    "preview": "import { computed, ref } from 'vue'\nimport type { Bridge } from '@vue-devtools/shared-utils'\nimport { BridgeEvents } fro"
  },
  {
    "path": "packages/app-frontend/src/features/apps/vue-version-check.ts",
    "chars": 863,
    "preview": "import { onMounted, ref } from 'vue'\nimport semver from 'semver'\n\nconst packageData = ref<any>(null)\n\nexport function us"
  },
  {
    "path": "packages/app-frontend/src/features/bridge/index.ts",
    "chars": 1154,
    "preview": "import { onUnmounted } from 'vue'\nimport type { Bridge } from '@vue-devtools/shared-utils'\nimport { BridgeEvents } from "
  },
  {
    "path": "packages/app-frontend/src/features/chrome/index.ts",
    "chars": 34,
    "preview": "export * from './pane-visibility'\n"
  },
  {
    "path": "packages/app-frontend/src/features/chrome/pane-visibility.ts",
    "chars": 675,
    "preview": "import { isChrome } from '@vue-devtools/shared-utils'\n\nlet panelShown = !isChrome\nlet pendingAction: (() => void | Promi"
  },
  {
    "path": "packages/app-frontend/src/features/code/CodeEditor.vue",
    "chars": 3713,
    "preview": "<script>\n// Fork of https://github.com/egoist/vue-monaco/\nimport * as monaco from 'monaco-editor'\nimport assign from 'lo"
  },
  {
    "path": "packages/app-frontend/src/features/components/ComponentTreeNode.vue",
    "chars": 9442,
    "preview": "<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, onMounted, ref, toRefs, watch"
  },
  {
    "path": "packages/app-frontend/src/features/components/ComponentsInspector.vue",
    "chars": 7993,
    "preview": "<script lang=\"ts\">\nimport SplitPane from '@front/features/layout/SplitPane.vue'\nimport { defineComponent, onMounted, pro"
  },
  {
    "path": "packages/app-frontend/src/features/components/RenderCode.vue",
    "chars": 1723,
    "preview": "<script>\nimport { defineAsyncComponent, reactive, watch } from 'vue'\nimport { BridgeEvents } from '@vue-devtools/shared-"
  },
  {
    "path": "packages/app-frontend/src/features/components/SelectedComponentPane.vue",
    "chars": 4440,
    "preview": "<script lang=\"ts\">\nimport StateInspector from '@front/features/inspector/StateInspector.vue'\nimport EmptyPane from '@fro"
  },
  {
    "path": "packages/app-frontend/src/features/components/composable/components.ts",
    "chars": 11730,
    "preview": "import type { Ref } from 'vue'\nimport { computed, onMounted, ref, watch } from 'vue'\nimport type { ComponentTreeNode, Ed"
  },
  {
    "path": "packages/app-frontend/src/features/components/composable/highlight.ts",
    "chars": 619,
    "preview": "import { getBridge } from '@front/features/bridge'\nimport { BridgeEvents } from '@vue-devtools/shared-utils'\nimport type"
  },
  {
    "path": "packages/app-frontend/src/features/components/composable/index.ts",
    "chars": 104,
    "preview": "export * from './components'\nexport * from './highlight'\nexport * from './pick'\nexport * from './setup'\n"
  },
  {
    "path": "packages/app-frontend/src/features/components/composable/pick.ts",
    "chars": 1185,
    "preview": "import { ref } from 'vue'\nimport { BridgeEvents } from '@vue-devtools/shared-utils'\nimport { useBridge } from '@front/fe"
  },
  {
    "path": "packages/app-frontend/src/features/components/composable/setup.ts",
    "chars": 2539,
    "preview": "import type { Bridge } from '@vue-devtools/shared-utils'\nimport { BridgeEvents, getStorage, parse } from '@vue-devtools/"
  },
  {
    "path": "packages/app-frontend/src/features/connection/AppConnecting.vue",
    "chars": 866,
    "preview": "<template>\n  <div class=\"app-connecting w-full h-full flex items-center justify-center bg-white dark:bg-gray-800\">\n    <"
  },
  {
    "path": "packages/app-frontend/src/features/connection/AppDisconnected.vue",
    "chars": 194,
    "preview": "<template>\n  <div class=\"w-full h-full flex items-center justify-center\">\n    <VueIcon\n      icon=\"code_off\"\n      class"
  },
  {
    "path": "packages/app-frontend/src/features/connection/index.ts",
    "chars": 1293,
    "preview": "import { computed, ref } from 'vue'\nimport { useNow } from '@vueuse/core'\n\nconst isConnected = ref(false)\nconst isInitia"
  },
  {
    "path": "packages/app-frontend/src/features/error/ErrorOverlay.vue",
    "chars": 798,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\nimport { useError } from '.'\n\nexport default defineComponent({\n"
  },
  {
    "path": "packages/app-frontend/src/features/error/index.ts",
    "chars": 539,
    "preview": "import { computed, ref } from 'vue'\n\nexport interface ErrorMessage {\n  message: string\n  icon: string\n}\n\nconst errors = "
  },
  {
    "path": "packages/app-frontend/src/features/header/AppHeader.vue",
    "chars": 6684,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport type { RouteLocation, RouteLocatio"
  },
  {
    "path": "packages/app-frontend/src/features/header/AppHeaderSelect.vue",
    "chars": 5923,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, ref } from 'vue'\nimport { SharedData } from '@vue-devtools/shared"
  },
  {
    "path": "packages/app-frontend/src/features/header/AppHistoryNav.vue",
    "chars": 284,
    "preview": "<template>\n  <div class=\"flex\">\n    <VueButton\n      icon-left=\"arrow_back\"\n      class=\"icon-button flat\"\n      @click="
  },
  {
    "path": "packages/app-frontend/src/features/header/header.ts",
    "chars": 69,
    "preview": "import { ref } from 'vue'\n\nexport const showAppsSelector = ref(true)\n"
  },
  {
    "path": "packages/app-frontend/src/features/header/tabs.ts",
    "chars": 371,
    "preview": "import { computed } from 'vue'\nimport { useRoute } from 'vue-router'\n\nexport function useTabs() {\n  const route = useRou"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/DataField.vue",
    "chars": 19706,
    "preview": "<script lang=\"ts\">\n/* eslint-disable vue/no-unused-refs */\n\nimport { defineComponent, toRaw } from 'vue'\nimport {\n  Brid"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/StateFields.vue",
    "chars": 3369,
    "preview": "<script>\nimport DataField from './DataField.vue'\n\nexport default {\n  components: {\n    DataField,\n  },\n\n  props: {\n    f"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/StateInspector.vue",
    "chars": 2792,
    "preview": "<script>\nimport { useDefer } from '@front/util/defer'\nimport { getStorage, setStorage } from '@vue-devtools/shared-utils"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/StateType.vue",
    "chars": 3766,
    "preview": "<script lang=\"ts\">\nimport PluginSourceIcon from '@front/features/plugin/PluginSourceIcon.vue'\n\nimport { computed, define"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/custom/CustomInspector.vue",
    "chars": 4460,
    "preview": "<script lang=\"ts\">\nimport SplitPane from '@front/features/layout/SplitPane.vue'\nimport EmptyPane from '@front/features/l"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/custom/CustomInspectorNode.vue",
    "chars": 5461,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport scrollIntoView from 'scroll-into-v"
  },
  {
    "path": "packages/app-frontend/src/features/inspector/custom/CustomInspectorSelectedNodePane.vue",
    "chars": 2557,
    "preview": "<script lang=\"ts\">\nimport EmptyPane from '@front/features/layout/EmptyPane.vue'\n\nimport { defineComponent, ref, watch } "
  },
  {
    "path": "packages/app-frontend/src/features/inspector/custom/composable.ts",
    "chars": 5765,
    "preview": "import { computed, ref } from 'vue'\nimport { useRoute } from 'vue-router'\nimport { useApps } from '@front/features/apps'"
  },
  {
    "path": "packages/app-frontend/src/features/layout/EmptyPane.vue",
    "chars": 454,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n  props: {\n    icon: {\n      "
  },
  {
    "path": "packages/app-frontend/src/features/layout/SplitPane.vue",
    "chars": 8002,
    "preview": "<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimpor"
  },
  {
    "path": "packages/app-frontend/src/features/layout/orientation.ts",
    "chars": 441,
    "preview": "import { ref } from 'vue'\n\nconst orientation = ref('landscape')\n\nexport function useOrientation() {\n  return {\n    orien"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginDetails.vue",
    "chars": 3436,
    "preview": "<script lang=\"ts\">\nimport EmptyPane from '@front/features/layout/EmptyPane.vue'\nimport SplitPane from '@front/features/l"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginHome.vue",
    "chars": 1030,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n  props: {\n    noPlugins: {\n "
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginListItem.vue",
    "chars": 1144,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent } from 'vue'\nimport { PluginPermission, hasPluginPermission } from"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginPermission.vue",
    "chars": 994,
    "preview": "<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent } from 'vue'\nimport type { Plu"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginSettings.vue",
    "chars": 1912,
    "preview": "<script lang=\"ts\">\nimport EmptyPane from '@front/features/layout/EmptyPane.vue'\n\nimport type { PropType } from 'vue'\nimp"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginSettingsItem.vue",
    "chars": 2418,
    "preview": "<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent } from 'vue'\nimport type { Plu"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginSourceDescription.vue",
    "chars": 1286,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { usePl"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/PluginSourceIcon.vue",
    "chars": 894,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\nimport { useRouter } from 'vue-router'\nimport PluginSourceDescr"
  },
  {
    "path": "packages/app-frontend/src/features/plugin/Plugins.vue",
    "chars": 2091,
    "preview": "<script lang=\"ts\">\nimport SplitPane from '@front/features/layout/SplitPane.vue'\nimport { computed, defineComponent, ref "
  },
  {
    "path": "packages/app-frontend/src/features/plugin/index.ts",
    "chars": 1998,
    "preview": "import { computed, ref } from 'vue'\nimport type { PluginDescriptor } from '@vue/devtools-api'\nimport type { Bridge } fro"
  },
  {
    "path": "packages/app-frontend/src/features/settings/GlobalSettings.vue",
    "chars": 5931,
    "preview": "<script lang=\"ts\" setup>\nimport { ref } from 'vue'\nimport { useLocalStorage } from '@vueuse/core'\nimport { clearStorage "
  },
  {
    "path": "packages/app-frontend/src/features/settings/NewTag.vue",
    "chars": 579,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n  inject: [\n    'currentSetti"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/AskScreenshotPermission.vue",
    "chars": 1576,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\nimport { SharedData } from '@vue-devtools/shared-utils'\n\nexport"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/LayerItem.vue",
    "chars": 4852,
    "preview": "<script lang=\"ts\">\nimport PluginSourceIcon from '@front/features/plugin/PluginSourceIcon.vue'\n\nimport type { PropType } "
  },
  {
    "path": "packages/app-frontend/src/features/timeline/Timeline.vue",
    "chars": 17321,
    "preview": "<script lang=\"ts\">\nimport SplitPane from '@front/features/layout/SplitPane.vue'\nimport PluginSourceIcon from '@front/fea"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/TimelineEventInspector.vue",
    "chars": 3908,
    "preview": "<script lang=\"ts\">\nimport StateInspector from '@front/features/inspector/StateInspector.vue'\nimport EmptyPane from '@fro"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/TimelineEventList.vue",
    "chars": 6907,
    "preview": "<script lang=\"ts\">\nimport EmptyPane from '@front/features/layout/EmptyPane.vue'\n\nimport { computed, defineComponent, ref"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/TimelineEventListItem.vue",
    "chars": 2525,
    "preview": "<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent } from 'vue'\nimport { formatTi"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/TimelineScrollbar.vue",
    "chars": 5168,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, onUnmounted, ref } from 'vue'\n\nexport default defineComponent({\n "
  },
  {
    "path": "packages/app-frontend/src/features/timeline/TimelineView.vue",
    "chars": 41779,
    "preview": "<script lang=\"ts\">\nimport * as PIXI from 'pixi.js-legacy'\nimport { install as installUnsafeEval } from '@pixi/unsafe-eva"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/events.ts",
    "chars": 4124,
    "preview": "import { computed, onUnmounted, watch } from 'vue'\nimport { BridgeEvents, setStorage } from '@vue-devtools/shared-utils'"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/index.ts",
    "chars": 200,
    "preview": "export * from './events'\nexport * from './layers'\nexport * from './markers'\nexport * from './reset'\nexport * from './scr"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/layers.ts",
    "chars": 4288,
    "preview": "import { computed } from 'vue'\nimport { BridgeEvents, setStorage } from '@vue-devtools/shared-utils'\nimport { useApps, w"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/markers.ts",
    "chars": 664,
    "preview": "import { useCurrentApp } from '@front/features/apps'\nimport { computed, watch } from 'vue'\nimport { getBridge } from '@f"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/reset.ts",
    "chars": 1442,
    "preview": "import { onUnmounted } from 'vue'\nimport { BridgeEvents, getStorage } from '@vue-devtools/shared-utils'\nimport { getBrid"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/screenshot.ts",
    "chars": 2634,
    "preview": "import { BridgeEvents, SharedData } from '@vue-devtools/shared-utils'\nimport { useApps } from '@front/features/apps'\nimp"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/setup.ts",
    "chars": 4280,
    "preview": "import type { Bridge } from '@vue-devtools/shared-utils'\nimport { BridgeEvents, parse } from '@vue-devtools/shared-utils"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/store.ts",
    "chars": 2704,
    "preview": "import type { Ref } from 'vue'\nimport { ref } from 'vue'\nimport type { ID } from '@vue/devtools-api'\nimport type * as PI"
  },
  {
    "path": "packages/app-frontend/src/features/timeline/composable/time.ts",
    "chars": 250,
    "preview": "import {\n  cursorTime,\n  endTime,\n  maxTime,\n  minTime,\n  startTime,\n} from './store'\n\nexport function useTime() {\n  ret"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueButton.vue",
    "chars": 2478,
    "preview": "<script lang=\"ts\">\nimport type { PropType } from 'vue'\nimport { computed, defineComponent, useAttrs } from 'vue'\nimport "
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueDisable.vue",
    "chars": 1022,
    "preview": "<script>\nimport { computed, defineComponent, h } from 'vue'\nimport { useDisabledChild, useDisabledParent } from '../comp"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueDropdown.vue",
    "chars": 4215,
    "preview": "<script lang=\"ts\">\nimport {\n  computed,\n  defineComponent,\n  nextTick,\n  onBeforeUnmount,\n  onMounted,\n  ref,\n  watch,\n}"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueDropdownButton.vue",
    "chars": 263,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n  name: 'VueDropdownButton',\n"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueFormField.vue",
    "chars": 1783,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, provide, reactive } from 'vue'\n\nconst statusIcons = {\n  danger: '"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueGroup.vue",
    "chars": 2558,
    "preview": "<script lang=\"ts\">\nimport {\n  computed,\n  defineComponent,\n  nextTick,\n  onMounted,\n  provide,\n  ref,\n  useSlots,\n  watc"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueGroupButton.vue",
    "chars": 1142,
    "preview": "<script lang=\"ts\">\nimport type { Ref } from 'vue'\nimport { computed, defineComponent, inject, watch } from 'vue'\n\nexport"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueIcon.vue",
    "chars": 308,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n  props: {\n    icon: {\n      "
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueInput.vue",
    "chars": 8832,
    "preview": "<script lang=\"ts\">\nimport type {\n  UnwrapNestedRefs,\n} from 'vue'\nimport {\n  computed,\n  defineComponent,\n  inject,\n  re"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueLoadingBar.vue",
    "chars": 478,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n  name: 'VueLoadingBar',\n  pr"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueLoadingIndicator.vue",
    "chars": 116,
    "preview": "<template>\n  <div class=\"vue-ui-loading-indicator\">\n    <div class=\"animation\" />\n    <slot />\n  </div>\n</template>\n"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueModal.vue",
    "chars": 1773,
    "preview": "<script lang=\"ts\">\n/* eslint-disable vue/no-unused-refs */\n\nimport { defineComponent, nextTick, onMounted, ref } from 'v"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueSelect.vue",
    "chars": 1760,
    "preview": "<script lang=\"ts\">\nimport type { ComponentInternalInstance } from 'vue'\nimport { computed, defineComponent, provide, ref"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueSelectButton.vue",
    "chars": 1186,
    "preview": "<script lang=\"ts\">\nimport type { ComponentInternalInstance, Ref } from 'vue'\nimport { defineComponent, getCurrentInstanc"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/VueSwitch.vue",
    "chars": 1689,
    "preview": "<script lang=\"ts\">\nimport { computed, defineComponent, ref } from 'vue'\nimport { useDisabledChild } from '../composables"
  },
  {
    "path": "packages/app-frontend/src/features/ui/components/icons.ts",
    "chars": 1138,
    "preview": "const icons = require.context(\n  '@akryum/md-icons-svg/svg/',\n  true,\n  /materialicons\\/24px\\.svg$/,\n)\n\nexport default {"
  },
  {
    "path": "packages/app-frontend/src/features/ui/composables/useDisableScroll.ts",
    "chars": 584,
    "preview": "import { onBeforeUnmount, onMounted } from 'vue'\n\nlet count = 0\n\nfunction getScrollingElements() {\n  return document.que"
  },
  {
    "path": "packages/app-frontend/src/features/ui/composables/useDisabled.ts",
    "chars": 868,
    "preview": "/**\n * (Use with the DisabledChild mixin)\n * Allow disabling an entire tree of components implementing the DisabledChild"
  },
  {
    "path": "packages/app-frontend/src/features/ui/index.ts",
    "chars": 2106,
    "preview": "import type { Plugin } from 'vue'\nimport FloatingVue from 'floating-vue'\nimport VueIcons from './components/icons'\nimpor"
  }
]

// ... and 204 more files (download for full content)

About this extraction

This page contains the full source code of the vuejs/devtools-v6 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 404 files (910.8 KB), approximately 248.1k tokens, and a symbol index with 775 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!