From e16612698b27ca14527b1cec2e55f6cffc83aa46 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 8 Apr 2026 12:40:10 +1000 Subject: [PATCH 1/7] feat: vite devtools integration Add devtools and devtools-app packages with Vue composables and plugin updates. --- examples/vite-ssr-vue/vite.config.ts | 6 +- packages/devtools-app/.nuxt/app.config.mjs | 317 ++++ ...aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff | 0 ...R_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff | 0 ...you9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff | 0 ...gqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff | 0 ...O5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff | 0 ...l5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff | 0 ..._cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff | 0 ...9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff | 0 ...a0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff | 0 ...ZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff | 0 ...6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff | 0 ...Z24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff | 0 ...nTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 | 0 ...HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 | 0 ...YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 | 0 ...PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 | 0 ...3svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 | 0 ...z3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 | 0 ...L_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 | 0 ...Vrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 | 0 ...hd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 | 0 ...eLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 | 0 ...O3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 | 0 ...QdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 | 0 ...lNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 | 0 packages/devtools-app/.nuxt/components.d.ts | 322 ++++ packages/devtools-app/.nuxt/imports.d.ts | 49 + .../096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json | 1 + .../0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json | 1 + .../381b0558-98b7-448d-9168-38957e42e433.json | 1 + .../4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json | 1 + .../8251125c-23c7-4428-a540-3c662e362e93.json | 1 + .../c2629906-49d7-48f6-8234-c88fca0b6c44.json | 1 + .../cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json | 1 + .../devtools-app/.nuxt/nuxt-fonts-global.css | 0 .../.nuxt/nuxt-icon-client-bundle.mjs | 1 + .../.nuxt/nuxt-icon-server-bundle.mjs | 13 + packages/devtools-app/.nuxt/nuxt.d.ts | 24 + packages/devtools-app/.nuxt/nuxt.node.d.ts | 14 + packages/devtools-app/.nuxt/nuxt.shared.d.ts | 6 + .../.nuxt/prerender/chunks/_/error-500.mjs | 19 + .../prerender/chunks/_/error-500.mjs.map | 1 + .../.nuxt/prerender/chunks/_/renderer.mjs | 425 +++++ .../.nuxt/prerender/chunks/_/renderer.mjs.map | 1 + .../chunks/build/client.precomputed.mjs | 4 + .../chunks/build/client.precomputed.mjs.map | 1 + .../.nuxt/prerender/chunks/nitro/nitro.mjs | 1596 +++++++++++++++++ .../prerender/chunks/nitro/nitro.mjs.map | 1 + .../chunks/virtual/_virtual_spa-template.mjs | 4 + .../virtual/_virtual_spa-template.mjs.map | 1 + .../devtools-app/.nuxt/prerender/index.mjs | 24 + .../.nuxt/prerender/index.mjs.map | 1 + .../.nuxt/schema/nuxt.schema.d.ts | 210 +++ .../.nuxt/schema/nuxt.schema.json | 263 +++ packages/devtools-app/.nuxt/tsconfig.app.json | 232 +++ packages/devtools-app/.nuxt/tsconfig.json | 235 +++ .../devtools-app/.nuxt/tsconfig.node.json | 127 ++ .../devtools-app/.nuxt/tsconfig.server.json | 164 ++ .../devtools-app/.nuxt/tsconfig.shared.json | 164 ++ .../devtools-app/.nuxt/types/app.config.d.ts | 331 ++++ packages/devtools-app/.nuxt/types/build.d.ts | 25 + .../devtools-app/.nuxt/types/builder-env.d.ts | 1 + .../devtools-app/.nuxt/types/components.d.ts | 327 ++++ .../devtools-app/.nuxt/types/imports.d.ts | 866 +++++++++ .../devtools-app/.nuxt/types/layouts.d.ts | 19 + .../devtools-app/.nuxt/types/middleware.d.ts | 7 + .../devtools-app/.nuxt/types/modules.d.ts | 139 ++ .../.nuxt/types/nitro-config.d.ts | 14 + .../.nuxt/types/nitro-imports.d.ts | 149 ++ .../.nuxt/types/nitro-layouts.d.ts | 17 + .../.nuxt/types/nitro-middleware.d.ts | 11 + .../devtools-app/.nuxt/types/nitro-nuxt.d.ts | 64 + .../.nuxt/types/nitro-routes.d.ts | 17 + packages/devtools-app/.nuxt/types/nitro.d.ts | 3 + .../devtools-app/.nuxt/types/plugins.d.ts | 34 + .../.nuxt/types/runtime-config.d.ts | 36 + .../.nuxt/types/shared-imports.d.ts | 10 + packages/devtools-app/.nuxt/types/ui.d.ts | 36 + .../devtools-app/.nuxt/types/vue-shim.d.ts | 0 .../devtools-app/.nuxt/ui-image-component.ts | 1 + packages/devtools-app/.nuxt/ui.css | 156 ++ packages/devtools-app/.nuxt/ui/accordion.ts | 20 + packages/devtools-app/.nuxt/ui/alert.ts | 264 +++ packages/devtools-app/.nuxt/ui/auth-form.ts | 20 + .../devtools-app/.nuxt/ui/avatar-group.ts | 52 + packages/devtools-app/.nuxt/ui/avatar.ts | 54 + packages/devtools-app/.nuxt/ui/badge.ts | 263 +++ packages/devtools-app/.nuxt/ui/banner.ts | 108 ++ packages/devtools-app/.nuxt/ui/blog-post.ts | 143 ++ packages/devtools-app/.nuxt/ui/blog-posts.ts | 9 + packages/devtools-app/.nuxt/ui/breadcrumb.ts | 45 + .../devtools-app/.nuxt/ui/button-group.ts | 16 + packages/devtools-app/.nuxt/ui/button.ts | 378 ++++ packages/devtools-app/.nuxt/ui/calendar.ts | 331 ++++ packages/devtools-app/.nuxt/ui/card.ts | 34 + packages/devtools-app/.nuxt/ui/carousel.ts | 38 + .../.nuxt/ui/changelog-version.ts | 45 + .../.nuxt/ui/changelog-versions.ts | 8 + .../devtools-app/.nuxt/ui/chat-message.ts | 138 ++ .../devtools-app/.nuxt/ui/chat-messages.ts | 14 + .../devtools-app/.nuxt/ui/chat-palette.ts | 8 + .../.nuxt/ui/chat-prompt-submit.ts | 5 + packages/devtools-app/.nuxt/ui/chat-prompt.ts | 35 + .../devtools-app/.nuxt/ui/chat-reasoning.ts | 36 + .../devtools-app/.nuxt/ui/chat-shimmer.ts | 3 + packages/devtools-app/.nuxt/ui/chat-tool.ts | 61 + .../devtools-app/.nuxt/ui/checkbox-group.ts | 207 +++ packages/devtools-app/.nuxt/ui/checkbox.ts | 237 +++ packages/devtools-app/.nuxt/ui/chip.ts | 96 + packages/devtools-app/.nuxt/ui/collapsible.ts | 6 + .../devtools-app/.nuxt/ui/color-picker.ts | 47 + .../devtools-app/.nuxt/ui/command-palette.ts | 150 ++ packages/devtools-app/.nuxt/ui/container.ts | 3 + .../devtools-app/.nuxt/ui/context-menu.ts | 219 +++ .../devtools-app/.nuxt/ui/dashboard-group.ts | 3 + .../devtools-app/.nuxt/ui/dashboard-navbar.ts | 21 + .../devtools-app/.nuxt/ui/dashboard-panel.ts | 17 + .../.nuxt/ui/dashboard-resize-handle.ts | 3 + .../.nuxt/ui/dashboard-search-button.ts | 15 + .../devtools-app/.nuxt/ui/dashboard-search.ts | 31 + .../.nuxt/ui/dashboard-sidebar-collapse.ts | 9 + .../.nuxt/ui/dashboard-sidebar-toggle.ts | 9 + .../.nuxt/ui/dashboard-sidebar.ts | 37 + .../.nuxt/ui/dashboard-toolbar.ts | 7 + packages/devtools-app/.nuxt/ui/drawer.ts | 149 ++ .../devtools-app/.nuxt/ui/dropdown-menu.ts | 227 +++ .../.nuxt/ui/editor-drag-handle.ts | 6 + .../.nuxt/ui/editor-emoji-menu.ts | 78 + .../.nuxt/ui/editor-mention-menu.ts | 78 + .../.nuxt/ui/editor-suggestion-menu.ts | 78 + .../devtools-app/.nuxt/ui/editor-toolbar.ts | 21 + packages/devtools-app/.nuxt/ui/editor.ts | 55 + packages/devtools-app/.nuxt/ui/empty.ts | 83 + packages/devtools-app/.nuxt/ui/error.ts | 9 + packages/devtools-app/.nuxt/ui/field-group.ts | 16 + packages/devtools-app/.nuxt/ui/file-upload.ts | 290 +++ .../devtools-app/.nuxt/ui/footer-columns.ts | 28 + packages/devtools-app/.nuxt/ui/footer.ts | 11 + packages/devtools-app/.nuxt/ui/form-field.ts | 62 + packages/devtools-app/.nuxt/ui/form.ts | 3 + packages/devtools-app/.nuxt/ui/header.ts | 25 + packages/devtools-app/.nuxt/ui/index.ts | 113 ++ packages/devtools-app/.nuxt/ui/input-date.ts | 360 ++++ packages/devtools-app/.nuxt/ui/input-menu.ts | 484 +++++ .../devtools-app/.nuxt/ui/input-number.ts | 279 +++ packages/devtools-app/.nuxt/ui/input-tags.ts | 333 ++++ packages/devtools-app/.nuxt/ui/input-time.ts | 360 ++++ packages/devtools-app/.nuxt/ui/input.ts | 312 ++++ packages/devtools-app/.nuxt/ui/kbd.ts | 195 ++ packages/devtools-app/.nuxt/ui/link.ts | 22 + packages/devtools-app/.nuxt/ui/main.ts | 3 + packages/devtools-app/.nuxt/ui/marquee.ts | 66 + packages/devtools-app/.nuxt/ui/modal.ts | 60 + .../devtools-app/.nuxt/ui/navigation-menu.ts | 513 ++++++ .../devtools-app/.nuxt/ui/page-anchors.ts | 30 + packages/devtools-app/.nuxt/ui/page-aside.ts | 10 + packages/devtools-app/.nuxt/ui/page-body.ts | 3 + packages/devtools-app/.nuxt/ui/page-card.ts | 274 +++ .../devtools-app/.nuxt/ui/page-columns.ts | 3 + packages/devtools-app/.nuxt/ui/page-cta.ts | 70 + .../devtools-app/.nuxt/ui/page-feature.ts | 34 + packages/devtools-app/.nuxt/ui/page-grid.ts | 3 + packages/devtools-app/.nuxt/ui/page-header.ts | 18 + packages/devtools-app/.nuxt/ui/page-hero.ts | 44 + packages/devtools-app/.nuxt/ui/page-links.ts | 25 + packages/devtools-app/.nuxt/ui/page-list.ts | 8 + packages/devtools-app/.nuxt/ui/page-logos.ts | 15 + .../devtools-app/.nuxt/ui/page-section.ts | 84 + packages/devtools-app/.nuxt/ui/page.ts | 32 + packages/devtools-app/.nuxt/ui/pagination.ts | 13 + packages/devtools-app/.nuxt/ui/pin-input.ts | 194 ++ packages/devtools-app/.nuxt/ui/popover.ts | 6 + .../devtools-app/.nuxt/ui/pricing-plan.ts | 101 ++ .../devtools-app/.nuxt/ui/pricing-plans.ts | 22 + .../devtools-app/.nuxt/ui/pricing-table.ts | 51 + packages/devtools-app/.nuxt/ui/progress.ts | 297 +++ packages/devtools-app/.nuxt/ui/radio-group.ts | 352 ++++ packages/devtools-app/.nuxt/ui/scroll-area.ts | 21 + packages/devtools-app/.nuxt/ui/select-menu.ts | 385 ++++ packages/devtools-app/.nuxt/ui/select.ts | 371 ++++ packages/devtools-app/.nuxt/ui/separator.ts | 172 ++ packages/devtools-app/.nuxt/ui/sidebar.ts | 164 ++ packages/devtools-app/.nuxt/ui/skeleton.ts | 3 + packages/devtools-app/.nuxt/ui/slideover.ts | 132 ++ packages/devtools-app/.nuxt/ui/slider.ts | 171 ++ packages/devtools-app/.nuxt/ui/stepper.ts | 202 +++ packages/devtools-app/.nuxt/ui/switch.ts | 132 ++ packages/devtools-app/.nuxt/ui/table.ts | 162 ++ packages/devtools-app/.nuxt/ui/tabs.ts | 258 +++ packages/devtools-app/.nuxt/ui/textarea.ts | 317 ++++ packages/devtools-app/.nuxt/ui/timeline.ts | 321 ++++ packages/devtools-app/.nuxt/ui/toast.ts | 74 + packages/devtools-app/.nuxt/ui/toaster.ts | 91 + packages/devtools-app/.nuxt/ui/tooltip.ts | 9 + packages/devtools-app/.nuxt/ui/tree.ts | 168 ++ packages/devtools-app/.nuxt/ui/user.ts | 101 ++ packages/devtools-app/.output/nitro.json | 15 + packages/devtools-app/app.vue | 136 ++ packages/devtools-app/assets/css/global.css | 27 + packages/devtools-app/composables/rpc.ts | 33 + packages/devtools-app/composables/state.ts | 85 + packages/devtools-app/composables/tools.ts | 28 + packages/devtools-app/nuxt.config.ts | 51 + packages/devtools-app/package.json | 20 + packages/devtools-app/pages/identity.vue | 475 +++++ packages/devtools-app/pages/index.vue | 208 +++ packages/devtools-app/pages/schema.vue | 244 +++ packages/devtools-app/pages/scripts.vue | 106 ++ packages/devtools-app/pages/serp.vue | 182 ++ packages/devtools-app/tsconfig.json | 3 + .../devtools-app/utils/schema-validation.ts | 470 +++++ packages/devtools/build.config.ts | 18 + packages/devtools/package.json | 70 + packages/devtools/src/bridge.ts | 240 +++ packages/devtools/src/index.ts | 2 + packages/devtools/src/plugin.ts | 26 + .../devtools/src/rpc/functions/get-config.ts | 12 + packages/devtools/src/rpc/index.ts | 1 + packages/devtools/src/rpc/types.ts | 57 + packages/devtools/src/vite.ts | 169 ++ packages/devtools/tsconfig.json | 8 + packages/unhead/src/plugins/validate.ts | 3 + packages/unhead/src/types/head.ts | 5 + packages/unhead/src/types/tags.ts | 5 + packages/vue/src/composables.ts | 7 + packages/vue/src/index.ts | 3 +- packages/vue/src/utils.ts | 13 +- 229 files changed, 21721 insertions(+), 3 deletions(-) create mode 100644 packages/devtools-app/.nuxt/app.config.mjs create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-2IIYX0aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-6KAoyxR_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-995OEiyou9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-PB8l0aO5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-ReiF7Vl5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-XVisnD_cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-_Typ3t9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-q1FXcza0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-qPutQnZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-tzSwS_6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-vrK12kZ24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/4qBuU9MRVUlPZNPSF7Xom_sK8RBEnfYu-9VXFrdq8A8-8TDwLE1HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/6dYsbWUd_BpKJ7mdDihgOcya1gHXLpJBuMYXux3WMjE-q3fYNS8YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/Lr-hqqZZsYmCt0ITUlr1CUrWim9fsKvoDFZliMxgNHY-iTa_Yt_PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/OknHvWI6KtYn1JQBzX7eSpNDBQ8520F9TvSUJYkVf6A-xeZn9253svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/UA7OtwYHwGN_HjcVGTdmiQxUit7FlqkCwxVUWSeXVnQ-B4OXCFOL_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/du_wVi8LKnmpxxm4zZ_Cr6Hr261AwOLTlejGtcx8ebU-9181D4phd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/qxAYvKsXWeYv731eb-h5TRurcdIP_W44mpNdX-HABAk-zUDeMEFlNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 create mode 100644 packages/devtools-app/.nuxt/components.d.ts create mode 100644 packages/devtools-app/.nuxt/imports.d.ts create mode 100644 packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json create mode 100644 packages/devtools-app/.nuxt/nuxt-fonts-global.css create mode 100644 packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs create mode 100644 packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs create mode 100644 packages/devtools-app/.nuxt/nuxt.d.ts create mode 100644 packages/devtools-app/.nuxt/nuxt.node.d.ts create mode 100644 packages/devtools-app/.nuxt/nuxt.shared.d.ts create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/renderer.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/renderer.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/build/client.precomputed.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/build/client.precomputed.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/nitro/nitro.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/nitro/nitro.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/virtual/_virtual_spa-template.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/virtual/_virtual_spa-template.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/index.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/index.mjs.map create mode 100644 packages/devtools-app/.nuxt/schema/nuxt.schema.d.ts create mode 100644 packages/devtools-app/.nuxt/schema/nuxt.schema.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.app.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.node.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.server.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.shared.json create mode 100644 packages/devtools-app/.nuxt/types/app.config.d.ts create mode 100644 packages/devtools-app/.nuxt/types/build.d.ts create mode 100644 packages/devtools-app/.nuxt/types/builder-env.d.ts create mode 100644 packages/devtools-app/.nuxt/types/components.d.ts create mode 100644 packages/devtools-app/.nuxt/types/imports.d.ts create mode 100644 packages/devtools-app/.nuxt/types/layouts.d.ts create mode 100644 packages/devtools-app/.nuxt/types/middleware.d.ts create mode 100644 packages/devtools-app/.nuxt/types/modules.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-config.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-imports.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-layouts.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-middleware.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-nuxt.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-routes.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro.d.ts create mode 100644 packages/devtools-app/.nuxt/types/plugins.d.ts create mode 100644 packages/devtools-app/.nuxt/types/runtime-config.d.ts create mode 100644 packages/devtools-app/.nuxt/types/shared-imports.d.ts create mode 100644 packages/devtools-app/.nuxt/types/ui.d.ts create mode 100644 packages/devtools-app/.nuxt/types/vue-shim.d.ts create mode 100644 packages/devtools-app/.nuxt/ui-image-component.ts create mode 100644 packages/devtools-app/.nuxt/ui.css create mode 100644 packages/devtools-app/.nuxt/ui/accordion.ts create mode 100644 packages/devtools-app/.nuxt/ui/alert.ts create mode 100644 packages/devtools-app/.nuxt/ui/auth-form.ts create mode 100644 packages/devtools-app/.nuxt/ui/avatar-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/avatar.ts create mode 100644 packages/devtools-app/.nuxt/ui/badge.ts create mode 100644 packages/devtools-app/.nuxt/ui/banner.ts create mode 100644 packages/devtools-app/.nuxt/ui/blog-post.ts create mode 100644 packages/devtools-app/.nuxt/ui/blog-posts.ts create mode 100644 packages/devtools-app/.nuxt/ui/breadcrumb.ts create mode 100644 packages/devtools-app/.nuxt/ui/button-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/button.ts create mode 100644 packages/devtools-app/.nuxt/ui/calendar.ts create mode 100644 packages/devtools-app/.nuxt/ui/card.ts create mode 100644 packages/devtools-app/.nuxt/ui/carousel.ts create mode 100644 packages/devtools-app/.nuxt/ui/changelog-version.ts create mode 100644 packages/devtools-app/.nuxt/ui/changelog-versions.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-message.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-messages.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-palette.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-prompt-submit.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-prompt.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-reasoning.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-shimmer.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-tool.ts create mode 100644 packages/devtools-app/.nuxt/ui/checkbox-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/checkbox.ts create mode 100644 packages/devtools-app/.nuxt/ui/chip.ts create mode 100644 packages/devtools-app/.nuxt/ui/collapsible.ts create mode 100644 packages/devtools-app/.nuxt/ui/color-picker.ts create mode 100644 packages/devtools-app/.nuxt/ui/command-palette.ts create mode 100644 packages/devtools-app/.nuxt/ui/container.ts create mode 100644 packages/devtools-app/.nuxt/ui/context-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-navbar.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-panel.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-resize-handle.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-search-button.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-search.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-sidebar-collapse.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-sidebar-toggle.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-sidebar.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-toolbar.ts create mode 100644 packages/devtools-app/.nuxt/ui/drawer.ts create mode 100644 packages/devtools-app/.nuxt/ui/dropdown-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-drag-handle.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-emoji-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-mention-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-suggestion-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-toolbar.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor.ts create mode 100644 packages/devtools-app/.nuxt/ui/empty.ts create mode 100644 packages/devtools-app/.nuxt/ui/error.ts create mode 100644 packages/devtools-app/.nuxt/ui/field-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/file-upload.ts create mode 100644 packages/devtools-app/.nuxt/ui/footer-columns.ts create mode 100644 packages/devtools-app/.nuxt/ui/footer.ts create mode 100644 packages/devtools-app/.nuxt/ui/form-field.ts create mode 100644 packages/devtools-app/.nuxt/ui/form.ts create mode 100644 packages/devtools-app/.nuxt/ui/header.ts create mode 100644 packages/devtools-app/.nuxt/ui/index.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-date.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-number.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-tags.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-time.ts create mode 100644 packages/devtools-app/.nuxt/ui/input.ts create mode 100644 packages/devtools-app/.nuxt/ui/kbd.ts create mode 100644 packages/devtools-app/.nuxt/ui/link.ts create mode 100644 packages/devtools-app/.nuxt/ui/main.ts create mode 100644 packages/devtools-app/.nuxt/ui/marquee.ts create mode 100644 packages/devtools-app/.nuxt/ui/modal.ts create mode 100644 packages/devtools-app/.nuxt/ui/navigation-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-anchors.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-aside.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-body.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-card.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-columns.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-cta.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-feature.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-grid.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-header.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-hero.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-links.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-list.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-logos.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-section.ts create mode 100644 packages/devtools-app/.nuxt/ui/page.ts create mode 100644 packages/devtools-app/.nuxt/ui/pagination.ts create mode 100644 packages/devtools-app/.nuxt/ui/pin-input.ts create mode 100644 packages/devtools-app/.nuxt/ui/popover.ts create mode 100644 packages/devtools-app/.nuxt/ui/pricing-plan.ts create mode 100644 packages/devtools-app/.nuxt/ui/pricing-plans.ts create mode 100644 packages/devtools-app/.nuxt/ui/pricing-table.ts create mode 100644 packages/devtools-app/.nuxt/ui/progress.ts create mode 100644 packages/devtools-app/.nuxt/ui/radio-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/scroll-area.ts create mode 100644 packages/devtools-app/.nuxt/ui/select-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/select.ts create mode 100644 packages/devtools-app/.nuxt/ui/separator.ts create mode 100644 packages/devtools-app/.nuxt/ui/sidebar.ts create mode 100644 packages/devtools-app/.nuxt/ui/skeleton.ts create mode 100644 packages/devtools-app/.nuxt/ui/slideover.ts create mode 100644 packages/devtools-app/.nuxt/ui/slider.ts create mode 100644 packages/devtools-app/.nuxt/ui/stepper.ts create mode 100644 packages/devtools-app/.nuxt/ui/switch.ts create mode 100644 packages/devtools-app/.nuxt/ui/table.ts create mode 100644 packages/devtools-app/.nuxt/ui/tabs.ts create mode 100644 packages/devtools-app/.nuxt/ui/textarea.ts create mode 100644 packages/devtools-app/.nuxt/ui/timeline.ts create mode 100644 packages/devtools-app/.nuxt/ui/toast.ts create mode 100644 packages/devtools-app/.nuxt/ui/toaster.ts create mode 100644 packages/devtools-app/.nuxt/ui/tooltip.ts create mode 100644 packages/devtools-app/.nuxt/ui/tree.ts create mode 100644 packages/devtools-app/.nuxt/ui/user.ts create mode 100644 packages/devtools-app/.output/nitro.json create mode 100644 packages/devtools-app/app.vue create mode 100644 packages/devtools-app/assets/css/global.css create mode 100644 packages/devtools-app/composables/rpc.ts create mode 100644 packages/devtools-app/composables/state.ts create mode 100644 packages/devtools-app/composables/tools.ts create mode 100644 packages/devtools-app/nuxt.config.ts create mode 100644 packages/devtools-app/package.json create mode 100644 packages/devtools-app/pages/identity.vue create mode 100644 packages/devtools-app/pages/index.vue create mode 100644 packages/devtools-app/pages/schema.vue create mode 100644 packages/devtools-app/pages/scripts.vue create mode 100644 packages/devtools-app/pages/serp.vue create mode 100644 packages/devtools-app/tsconfig.json create mode 100644 packages/devtools-app/utils/schema-validation.ts create mode 100644 packages/devtools/build.config.ts create mode 100644 packages/devtools/package.json create mode 100644 packages/devtools/src/bridge.ts create mode 100644 packages/devtools/src/index.ts create mode 100644 packages/devtools/src/plugin.ts create mode 100644 packages/devtools/src/rpc/functions/get-config.ts create mode 100644 packages/devtools/src/rpc/index.ts create mode 100644 packages/devtools/src/rpc/types.ts create mode 100644 packages/devtools/src/vite.ts create mode 100644 packages/devtools/tsconfig.json diff --git a/examples/vite-ssr-vue/vite.config.ts b/examples/vite-ssr-vue/vite.config.ts index 91591fc3f..be990b0b5 100644 --- a/examples/vite-ssr-vue/vite.config.ts +++ b/examples/vite-ssr-vue/vite.config.ts @@ -1,17 +1,21 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import { DevTools } from '@vitejs/devtools' import { unheadVueComposablesImports } from '@unhead/vue' import AutoImport from 'unplugin-auto-import/vite' +import { unheadDevtools } from '@unhead/devtools/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [ + DevTools({ clientAuth: false }), AutoImport({ imports: [ unheadVueComposablesImports, 'vue', ], }), - vue() + vue(), + unheadDevtools(), ], }) diff --git a/packages/devtools-app/.nuxt/app.config.mjs b/packages/devtools-app/.nuxt/app.config.mjs new file mode 100644 index 000000000..aab26c4a7 --- /dev/null +++ b/packages/devtools-app/.nuxt/app.config.mjs @@ -0,0 +1,317 @@ + +import { defuFn } from 'defu' + +const inlineConfig = { + "nuxt": {}, + "ui": { + "colors": { + "primary": "green", + "secondary": "blue", + "success": "green", + "info": "blue", + "warning": "yellow", + "error": "red", + "neutral": "slate" + }, + "icons": { + "arrowDown": "i-lucide-arrow-down", + "arrowLeft": "i-lucide-arrow-left", + "arrowRight": "i-lucide-arrow-right", + "arrowUp": "i-lucide-arrow-up", + "caution": "i-lucide-circle-alert", + "check": "i-lucide-check", + "chevronDoubleLeft": "i-lucide-chevrons-left", + "chevronDoubleRight": "i-lucide-chevrons-right", + "chevronDown": "i-lucide-chevron-down", + "chevronLeft": "i-lucide-chevron-left", + "chevronRight": "i-lucide-chevron-right", + "chevronUp": "i-lucide-chevron-up", + "close": "i-lucide-x", + "copy": "i-lucide-copy", + "copyCheck": "i-lucide-copy-check", + "dark": "i-lucide-moon", + "drag": "i-lucide-grip-vertical", + "ellipsis": "i-lucide-ellipsis", + "error": "i-lucide-circle-x", + "external": "i-lucide-arrow-up-right", + "eye": "i-lucide-eye", + "eyeOff": "i-lucide-eye-off", + "file": "i-lucide-file", + "folder": "i-lucide-folder", + "folderOpen": "i-lucide-folder-open", + "hash": "i-lucide-hash", + "info": "i-lucide-info", + "light": "i-lucide-sun", + "loading": "i-lucide-loader-circle", + "menu": "i-lucide-menu", + "minus": "i-lucide-minus", + "panelClose": "i-lucide-panel-left-close", + "panelOpen": "i-lucide-panel-left-open", + "plus": "i-lucide-plus", + "reload": "i-lucide-rotate-ccw", + "search": "i-lucide-search", + "stop": "i-lucide-square", + "success": "i-lucide-circle-check", + "system": "i-lucide-monitor", + "tip": "i-lucide-lightbulb", + "upload": "i-lucide-upload", + "warning": "i-lucide-triangle-alert" + }, + "tv": { + "twMergeConfig": {} + } + }, + "icon": { + "provider": "iconify", + "class": "", + "aliases": {}, + "iconifyApiEndpoint": "https://api.iconify.design", + "localApiEndpoint": "/api/_nuxt_icon", + "fallbackToApi": true, + "cssSelectorPrefix": "i-", + "cssWherePseudo": true, + "cssLayer": "base", + "mode": "css", + "attrs": { + "aria-hidden": true + }, + "collections": [ + "academicons", + "akar-icons", + "ant-design", + "arcticons", + "basil", + "bi", + "bitcoin-icons", + "bpmn", + "brandico", + "bx", + "bxl", + "bxs", + "bytesize", + "carbon", + "catppuccin", + "cbi", + "charm", + "ci", + "cib", + "cif", + "cil", + "circle-flags", + "circum", + "clarity", + "codex", + "codicon", + "covid", + "cryptocurrency", + "cryptocurrency-color", + "cuida", + "dashicons", + "devicon", + "devicon-plain", + "dinkie-icons", + "duo-icons", + "ei", + "el", + "emojione", + "emojione-monotone", + "emojione-v1", + "entypo", + "entypo-social", + "eos-icons", + "ep", + "et", + "eva", + "f7", + "fa", + "fa-brands", + "fa-regular", + "fa-solid", + "fa6-brands", + "fa6-regular", + "fa6-solid", + "fa7-brands", + "fa7-regular", + "fa7-solid", + "fad", + "famicons", + "fe", + "feather", + "file-icons", + "flag", + "flagpack", + "flat-color-icons", + "flat-ui", + "flowbite", + "fluent", + "fluent-color", + "fluent-emoji", + "fluent-emoji-flat", + "fluent-emoji-high-contrast", + "fluent-mdl2", + "fontelico", + "fontisto", + "formkit", + "foundation", + "fxemoji", + "gala", + "game-icons", + "garden", + "geo", + "gg", + "gis", + "gravity-ui", + "gridicons", + "grommet-icons", + "guidance", + "healthicons", + "heroicons", + "heroicons-outline", + "heroicons-solid", + "hugeicons", + "humbleicons", + "ic", + "icomoon-free", + "icon-park", + "icon-park-outline", + "icon-park-solid", + "icon-park-twotone", + "iconamoon", + "iconoir", + "icons8", + "il", + "ion", + "iwwa", + "ix", + "jam", + "la", + "lets-icons", + "line-md", + "lineicons", + "logos", + "ls", + "lsicon", + "lucide", + "lucide-lab", + "mage", + "majesticons", + "maki", + "map", + "marketeq", + "material-icon-theme", + "material-symbols", + "material-symbols-light", + "mdi", + "mdi-light", + "medical-icon", + "memory", + "meteocons", + "meteor-icons", + "mi", + "mingcute", + "mono-icons", + "mynaui", + "nimbus", + "nonicons", + "noto", + "noto-v1", + "nrk", + "octicon", + "oi", + "ooui", + "openmoji", + "oui", + "pajamas", + "pepicons", + "pepicons-pencil", + "pepicons-pop", + "pepicons-print", + "ph", + "picon", + "pixel", + "pixelarticons", + "prime", + "proicons", + "ps", + "qlementine-icons", + "quill", + "radix-icons", + "raphael", + "ri", + "rivet-icons", + "roentgen", + "si", + "si-glyph", + "sidekickicons", + "simple-icons", + "simple-line-icons", + "skill-icons", + "solar", + "stash", + "streamline", + "streamline-block", + "streamline-color", + "streamline-cyber", + "streamline-cyber-color", + "streamline-emojis", + "streamline-flex", + "streamline-flex-color", + "streamline-freehand", + "streamline-freehand-color", + "streamline-kameleon-color", + "streamline-logos", + "streamline-pixel", + "streamline-plump", + "streamline-plump-color", + "streamline-sharp", + "streamline-sharp-color", + "streamline-stickies-color", + "streamline-ultimate", + "streamline-ultimate-color", + "subway", + "svg-spinners", + "system-uicons", + "tabler", + "tdesign", + "teenyicons", + "temaki", + "token", + "token-branded", + "topcoat", + "twemoji", + "typcn", + "uil", + "uim", + "uis", + "uit", + "uiw", + "unjs", + "vaadin", + "vs", + "vscode-icons", + "websymbol", + "weui", + "whh", + "wi", + "wpf", + "zmdi", + "zondicons" + ], + "fetchTimeout": 1500 + } +} + +/** client **/ +import { _replaceAppConfig } from '#app/config' + +// Vite - webpack is handled directly in #app/config +if (import.meta.dev && !import.meta.nitro && import.meta.hot) { + import.meta.hot.accept((newModule) => { + _replaceAppConfig(newModule.default) + }) +} +/** client-end **/ + + + +export default /*@__PURE__*/ defuFn(inlineConfig) diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-2IIYX0aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-2IIYX0aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-6KAoyxR_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-6KAoyxR_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-995OEiyou9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-995OEiyou9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-PB8l0aO5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-PB8l0aO5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-ReiF7Vl5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-ReiF7Vl5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-XVisnD_cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-XVisnD_cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-_Typ3t9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-_Typ3t9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-q1FXcza0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-q1FXcza0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-qPutQnZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-qPutQnZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-tzSwS_6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-tzSwS_6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-vrK12kZ24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-vrK12kZ24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 b/packages/devtools-app/.nuxt/cache/fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/4qBuU9MRVUlPZNPSF7Xom_sK8RBEnfYu-9VXFrdq8A8-8TDwLE1HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 b/packages/devtools-app/.nuxt/cache/fonts/4qBuU9MRVUlPZNPSF7Xom_sK8RBEnfYu-9VXFrdq8A8-8TDwLE1HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/6dYsbWUd_BpKJ7mdDihgOcya1gHXLpJBuMYXux3WMjE-q3fYNS8YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 b/packages/devtools-app/.nuxt/cache/fonts/6dYsbWUd_BpKJ7mdDihgOcya1gHXLpJBuMYXux3WMjE-q3fYNS8YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/Lr-hqqZZsYmCt0ITUlr1CUrWim9fsKvoDFZliMxgNHY-iTa_Yt_PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 b/packages/devtools-app/.nuxt/cache/fonts/Lr-hqqZZsYmCt0ITUlr1CUrWim9fsKvoDFZliMxgNHY-iTa_Yt_PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/OknHvWI6KtYn1JQBzX7eSpNDBQ8520F9TvSUJYkVf6A-xeZn9253svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 b/packages/devtools-app/.nuxt/cache/fonts/OknHvWI6KtYn1JQBzX7eSpNDBQ8520F9TvSUJYkVf6A-xeZn9253svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 b/packages/devtools-app/.nuxt/cache/fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/UA7OtwYHwGN_HjcVGTdmiQxUit7FlqkCwxVUWSeXVnQ-B4OXCFOL_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 b/packages/devtools-app/.nuxt/cache/fonts/UA7OtwYHwGN_HjcVGTdmiQxUit7FlqkCwxVUWSeXVnQ-B4OXCFOL_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 b/packages/devtools-app/.nuxt/cache/fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/du_wVi8LKnmpxxm4zZ_Cr6Hr261AwOLTlejGtcx8ebU-9181D4phd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 b/packages/devtools-app/.nuxt/cache/fonts/du_wVi8LKnmpxxm4zZ_Cr6Hr261AwOLTlejGtcx8ebU-9181D4phd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 b/packages/devtools-app/.nuxt/cache/fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 b/packages/devtools-app/.nuxt/cache/fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 b/packages/devtools-app/.nuxt/cache/fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/qxAYvKsXWeYv731eb-h5TRurcdIP_W44mpNdX-HABAk-zUDeMEFlNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 b/packages/devtools-app/.nuxt/cache/fonts/qxAYvKsXWeYv731eb-h5TRurcdIP_W44mpNdX-HABAk-zUDeMEFlNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/components.d.ts b/packages/devtools-app/.nuxt/components.d.ts new file mode 100644 index 000000000..12e2dd1f6 --- /dev/null +++ b/packages/devtools-app/.nuxt/components.d.ts @@ -0,0 +1,322 @@ + +import type { DefineComponent, SlotsType } from 'vue' +type IslandComponent = DefineComponent<{}, {refresh: () => Promise}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T + +type HydrationStrategies = { + hydrateOnVisible?: IntersectionObserverInit | true + hydrateOnIdle?: number | true + hydrateOnInteraction?: keyof HTMLElementEventMap | Array | true + hydrateOnMediaQuery?: string + hydrateAfter?: number + hydrateWhen?: boolean + hydrateNever?: true +} +type LazyComponent = DefineComponent void }> & T + + +export const UColorModeAvatar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeAvatar.vue")['default'] +export const UColorModeButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeButton.vue")['default'] +export const UColorModeImage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeImage.vue")['default'] +export const UColorModeSelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSelect.vue")['default'] +export const UColorModeSwitch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSwitch.vue")['default'] +export const UAccordion: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Accordion.vue")['default'] +export const UAlert: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue")['default'] +export const UApp: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/App.vue")['default'] +export const UAuthForm: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AuthForm.vue")['default'] +export const UAvatar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Avatar.vue")['default'] +export const UAvatarGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AvatarGroup.vue")['default'] +export const UBadge: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue")['default'] +export const UBanner: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Banner.vue")['default'] +export const UBlogPost: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPost.vue")['default'] +export const UBlogPosts: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPosts.vue")['default'] +export const UBreadcrumb: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Breadcrumb.vue")['default'] +export const UButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Button.vue")['default'] +export const UCalendar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Calendar.vue")['default'] +export const UCard: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Card.vue")['default'] +export const UCarousel: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Carousel.vue")['default'] +export const UChangelogVersion: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersion.vue")['default'] +export const UChangelogVersions: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersions.vue")['default'] +export const UChatMessage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessage.vue")['default'] +export const UChatMessages: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessages.vue")['default'] +export const UChatPalette: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPalette.vue")['default'] +export const UChatPrompt: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPrompt.vue")['default'] +export const UChatPromptSubmit: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPromptSubmit.vue")['default'] +export const UChatReasoning: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatReasoning.vue")['default'] +export const UChatShimmer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatShimmer.vue")['default'] +export const UChatTool: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatTool.vue")['default'] +export const UCheckbox: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue")['default'] +export const UCheckboxGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CheckboxGroup.vue")['default'] +export const UChip: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Chip.vue")['default'] +export const UCollapsible: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Collapsible.vue")['default'] +export const UColorPicker: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ColorPicker.vue")['default'] +export const UCommandPalette: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CommandPalette.vue")['default'] +export const UContainer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Container.vue")['default'] +export const UContextMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue")['default'] +export const UContextMenuContent: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenuContent.vue")['default'] +export const UDashboardGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardGroup.vue")['default'] +export const UDashboardNavbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardNavbar.vue")['default'] +export const UDashboardPanel: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardPanel.vue")['default'] +export const UDashboardResizeHandle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardResizeHandle.vue")['default'] +export const UDashboardSearch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearch.vue")['default'] +export const UDashboardSearchButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearchButton.vue")['default'] +export const UDashboardSidebar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebar.vue")['default'] +export const UDashboardSidebarCollapse: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarCollapse.vue")['default'] +export const UDashboardSidebarToggle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarToggle.vue")['default'] +export const UDashboardToolbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardToolbar.vue")['default'] +export const UDrawer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue")['default'] +export const UDropdownMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue")['default'] +export const UDropdownMenuContent: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenuContent.vue")['default'] +export const UEditor: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Editor.vue")['default'] +export const UEditorDragHandle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorDragHandle.vue")['default'] +export const UEditorEmojiMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorEmojiMenu.vue")['default'] +export const UEditorMentionMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorMentionMenu.vue")['default'] +export const UEditorSuggestionMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorSuggestionMenu.vue")['default'] +export const UEditorToolbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorToolbar.vue")['default'] +export const UEmpty: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Empty.vue")['default'] +export const UError: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Error.vue")['default'] +export const UFieldGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FieldGroup.vue")['default'] +export const UFileUpload: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FileUpload.vue")['default'] +export const UFooter: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Footer.vue")['default'] +export const UFooterColumns: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FooterColumns.vue")['default'] +export const UForm: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Form.vue")['default'] +export const UFormField: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue")['default'] +export const UHeader: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Header.vue")['default'] +export const UIcon: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Icon.vue")['default'] +export const UInput: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Input.vue")['default'] +export const UInputDate: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputDate.vue")['default'] +export const UInputMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputMenu.vue")['default'] +export const UInputNumber: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue")['default'] +export const UInputTags: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTags.vue")['default'] +export const UInputTime: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTime.vue")['default'] +export const UKbd: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Kbd.vue")['default'] +export const ULink: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Link.vue")['default'] +export const ULinkBase: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/LinkBase.vue")['default'] +export const UMain: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Main.vue")['default'] +export const UMarquee: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Marquee.vue")['default'] +export const UModal: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue")['default'] +export const UNavigationMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue")['default'] +export const UOverlayProvider: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/OverlayProvider.vue")['default'] +export const UPage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Page.vue")['default'] +export const UPageAnchors: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAnchors.vue")['default'] +export const UPageAside: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAside.vue")['default'] +export const UPageBody: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageBody.vue")['default'] +export const UPageCTA: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCTA.vue")['default'] +export const UPageCard: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCard.vue")['default'] +export const UPageColumns: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageColumns.vue")['default'] +export const UPageFeature: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageFeature.vue")['default'] +export const UPageGrid: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageGrid.vue")['default'] +export const UPageHeader: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHeader.vue")['default'] +export const UPageHero: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHero.vue")['default'] +export const UPageLinks: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLinks.vue")['default'] +export const UPageList: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageList.vue")['default'] +export const UPageLogos: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLogos.vue")['default'] +export const UPageSection: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageSection.vue")['default'] +export const UPagination: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue")['default'] +export const UPinInput: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PinInput.vue")['default'] +export const UPopover: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue")['default'] +export const UPricingPlan: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlan.vue")['default'] +export const UPricingPlans: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlans.vue")['default'] +export const UPricingTable: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingTable.vue")['default'] +export const UProgress: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue")['default'] +export const URadioGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/RadioGroup.vue")['default'] +export const UScrollArea: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ScrollArea.vue")['default'] +export const USelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Select.vue")['default'] +export const USelectMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue")['default'] +export const USeparator: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Separator.vue")['default'] +export const USidebar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Sidebar.vue")['default'] +export const USkeleton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Skeleton.vue")['default'] +export const USlideover: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slideover.vue")['default'] +export const USlider: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slider.vue")['default'] +export const UStepper: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Stepper.vue")['default'] +export const USwitch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue")['default'] +export const UTable: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Table.vue")['default'] +export const UTabs: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue")['default'] +export const UTextarea: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue")['default'] +export const UTheme: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Theme.vue")['default'] +export const UTimeline: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Timeline.vue")['default'] +export const UToast: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toast.vue")['default'] +export const UToaster: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toaster.vue")['default'] +export const UTooltip: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue")['default'] +export const UTree: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tree.vue")['default'] +export const UUser: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/User.vue")['default'] +export const ULocaleSelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/locale/LocaleSelect.vue")['default'] +export const NuxtWelcome: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/welcome.vue")['default'] +export const NuxtLayout: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-layout")['default'] +export const NuxtErrorBoundary: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default'] +export const ClientOnly: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/client-only")['default'] +export const DevOnly: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/dev-only")['default'] +export const ServerPlaceholder: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/server-placeholder")['default'] +export const NuxtLink: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-link")['default'] +export const NuxtLoadingIndicator: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default'] +export const NuxtTime: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-time.vue")['default'] +export const NuxtRouteAnnouncer: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default'] +export const NuxtAnnouncer: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-announcer")['default'] +export const NuxtImg: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtImg'] +export const NuxtPicture: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtPicture'] +export const NuxtPage: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/pages/runtime/page")['default'] +export const NoScript: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['NoScript'] +export const Link: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Link'] +export const Base: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Base'] +export const Title: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Title'] +export const Meta: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Meta'] +export const Style: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Style'] +export const Head: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Head'] +export const Html: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Html'] +export const Body: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Body'] +export const Icon: typeof import("../../../node_modules/.pnpm/@nuxt+icon@2.2.1_magicast@0.5.2_vite@8.0.6_vue@3.5.32_typescript@6.0.2_/node_modules/@nuxt/icon/dist/runtime/components/index")['default'] +export const ColorScheme: typeof import("../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/component.vue3.vue")['default'] +export const NuxtIsland: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-island")['default'] +export const LazyUColorModeAvatar: LazyComponent +export const LazyUColorModeButton: LazyComponent +export const LazyUColorModeImage: LazyComponent +export const LazyUColorModeSelect: LazyComponent +export const LazyUColorModeSwitch: LazyComponent +export const LazyUAccordion: LazyComponent +export const LazyUAlert: LazyComponent +export const LazyUApp: LazyComponent +export const LazyUAuthForm: LazyComponent +export const LazyUAvatar: LazyComponent +export const LazyUAvatarGroup: LazyComponent +export const LazyUBadge: LazyComponent +export const LazyUBanner: LazyComponent +export const LazyUBlogPost: LazyComponent +export const LazyUBlogPosts: LazyComponent +export const LazyUBreadcrumb: LazyComponent +export const LazyUButton: LazyComponent +export const LazyUCalendar: LazyComponent +export const LazyUCard: LazyComponent +export const LazyUCarousel: LazyComponent +export const LazyUChangelogVersion: LazyComponent +export const LazyUChangelogVersions: LazyComponent +export const LazyUChatMessage: LazyComponent +export const LazyUChatMessages: LazyComponent +export const LazyUChatPalette: LazyComponent +export const LazyUChatPrompt: LazyComponent +export const LazyUChatPromptSubmit: LazyComponent +export const LazyUChatReasoning: LazyComponent +export const LazyUChatShimmer: LazyComponent +export const LazyUChatTool: LazyComponent +export const LazyUCheckbox: LazyComponent +export const LazyUCheckboxGroup: LazyComponent +export const LazyUChip: LazyComponent +export const LazyUCollapsible: LazyComponent +export const LazyUColorPicker: LazyComponent +export const LazyUCommandPalette: LazyComponent +export const LazyUContainer: LazyComponent +export const LazyUContextMenu: LazyComponent +export const LazyUContextMenuContent: LazyComponent +export const LazyUDashboardGroup: LazyComponent +export const LazyUDashboardNavbar: LazyComponent +export const LazyUDashboardPanel: LazyComponent +export const LazyUDashboardResizeHandle: LazyComponent +export const LazyUDashboardSearch: LazyComponent +export const LazyUDashboardSearchButton: LazyComponent +export const LazyUDashboardSidebar: LazyComponent +export const LazyUDashboardSidebarCollapse: LazyComponent +export const LazyUDashboardSidebarToggle: LazyComponent +export const LazyUDashboardToolbar: LazyComponent +export const LazyUDrawer: LazyComponent +export const LazyUDropdownMenu: LazyComponent +export const LazyUDropdownMenuContent: LazyComponent +export const LazyUEditor: LazyComponent +export const LazyUEditorDragHandle: LazyComponent +export const LazyUEditorEmojiMenu: LazyComponent +export const LazyUEditorMentionMenu: LazyComponent +export const LazyUEditorSuggestionMenu: LazyComponent +export const LazyUEditorToolbar: LazyComponent +export const LazyUEmpty: LazyComponent +export const LazyUError: LazyComponent +export const LazyUFieldGroup: LazyComponent +export const LazyUFileUpload: LazyComponent +export const LazyUFooter: LazyComponent +export const LazyUFooterColumns: LazyComponent +export const LazyUForm: LazyComponent +export const LazyUFormField: LazyComponent +export const LazyUHeader: LazyComponent +export const LazyUIcon: LazyComponent +export const LazyUInput: LazyComponent +export const LazyUInputDate: LazyComponent +export const LazyUInputMenu: LazyComponent +export const LazyUInputNumber: LazyComponent +export const LazyUInputTags: LazyComponent +export const LazyUInputTime: LazyComponent +export const LazyUKbd: LazyComponent +export const LazyULink: LazyComponent +export const LazyULinkBase: LazyComponent +export const LazyUMain: LazyComponent +export const LazyUMarquee: LazyComponent +export const LazyUModal: LazyComponent +export const LazyUNavigationMenu: LazyComponent +export const LazyUOverlayProvider: LazyComponent +export const LazyUPage: LazyComponent +export const LazyUPageAnchors: LazyComponent +export const LazyUPageAside: LazyComponent +export const LazyUPageBody: LazyComponent +export const LazyUPageCTA: LazyComponent +export const LazyUPageCard: LazyComponent +export const LazyUPageColumns: LazyComponent +export const LazyUPageFeature: LazyComponent +export const LazyUPageGrid: LazyComponent +export const LazyUPageHeader: LazyComponent +export const LazyUPageHero: LazyComponent +export const LazyUPageLinks: LazyComponent +export const LazyUPageList: LazyComponent +export const LazyUPageLogos: LazyComponent +export const LazyUPageSection: LazyComponent +export const LazyUPagination: LazyComponent +export const LazyUPinInput: LazyComponent +export const LazyUPopover: LazyComponent +export const LazyUPricingPlan: LazyComponent +export const LazyUPricingPlans: LazyComponent +export const LazyUPricingTable: LazyComponent +export const LazyUProgress: LazyComponent +export const LazyURadioGroup: LazyComponent +export const LazyUScrollArea: LazyComponent +export const LazyUSelect: LazyComponent +export const LazyUSelectMenu: LazyComponent +export const LazyUSeparator: LazyComponent +export const LazyUSidebar: LazyComponent +export const LazyUSkeleton: LazyComponent +export const LazyUSlideover: LazyComponent +export const LazyUSlider: LazyComponent +export const LazyUStepper: LazyComponent +export const LazyUSwitch: LazyComponent +export const LazyUTable: LazyComponent +export const LazyUTabs: LazyComponent +export const LazyUTextarea: LazyComponent +export const LazyUTheme: LazyComponent +export const LazyUTimeline: LazyComponent +export const LazyUToast: LazyComponent +export const LazyUToaster: LazyComponent +export const LazyUTooltip: LazyComponent +export const LazyUTree: LazyComponent +export const LazyUUser: LazyComponent +export const LazyULocaleSelect: LazyComponent +export const LazyNuxtWelcome: LazyComponent +export const LazyNuxtLayout: LazyComponent +export const LazyNuxtErrorBoundary: LazyComponent +export const LazyClientOnly: LazyComponent +export const LazyDevOnly: LazyComponent +export const LazyServerPlaceholder: LazyComponent +export const LazyNuxtLink: LazyComponent +export const LazyNuxtLoadingIndicator: LazyComponent +export const LazyNuxtTime: LazyComponent +export const LazyNuxtRouteAnnouncer: LazyComponent +export const LazyNuxtAnnouncer: LazyComponent +export const LazyNuxtImg: LazyComponent +export const LazyNuxtPicture: LazyComponent +export const LazyNuxtPage: LazyComponent +export const LazyNoScript: LazyComponent +export const LazyLink: LazyComponent +export const LazyBase: LazyComponent +export const LazyTitle: LazyComponent +export const LazyMeta: LazyComponent +export const LazyStyle: LazyComponent +export const LazyHead: LazyComponent +export const LazyHtml: LazyComponent +export const LazyBody: LazyComponent +export const LazyIcon: LazyComponent +export const LazyColorScheme: LazyComponent +export const LazyNuxtIsland: LazyComponent + +export const componentNames: string[] diff --git a/packages/devtools-app/.nuxt/imports.d.ts b/packages/devtools-app/.nuxt/imports.d.ts new file mode 100644 index 000000000..67ea0eb79 --- /dev/null +++ b/packages/devtools-app/.nuxt/imports.d.ts @@ -0,0 +1,49 @@ +export { useScriptTriggerConsent, useScriptEventPage, useScriptTriggerElement, useScript, useScriptGoogleAnalytics, useScriptPlausibleAnalytics, useScriptCrisp, useScriptClarity, useScriptCloudflareWebAnalytics, useScriptFathomAnalytics, useScriptMatomoAnalytics, useScriptGoogleTagManager, useScriptGoogleAdsense, useScriptSegment, useScriptMetaPixel, useScriptXPixel, useScriptIntercom, useScriptHotjar, useScriptStripe, useScriptLemonSqueezy, useScriptVimeoPlayer, useScriptYouTubePlayer, useScriptGoogleMaps, useScriptNpm, useScriptUmamiAnalytics, useScriptSnapchatPixel, useScriptRybbitAnalytics, useScriptDatabuddyAnalytics, useScriptRedditPixel, useScriptPayPal } from '#app/composables/script-stubs'; +export { isVue2, isVue3 } from 'vue-demi'; +export { defineNuxtLink } from '#app/components/nuxt-link'; +export { useNuxtApp, tryUseNuxtApp, defineNuxtPlugin, definePayloadPlugin, useRuntimeConfig, defineAppConfig } from '#app/nuxt'; +export { useAppConfig, updateAppConfig } from '#app/config'; +export { defineNuxtComponent } from '#app/composables/component'; +export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, createUseAsyncData } from '#app/composables/asyncData'; +export { useHydration } from '#app/composables/hydrate'; +export { callOnce } from '#app/composables/once'; +export { useState, clearNuxtState } from '#app/composables/state'; +export { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error'; +export { useFetch, useLazyFetch, createUseFetch } from '#app/composables/fetch'; +export { useCookie, refreshCookie } from '#app/composables/cookie'; +export { onPrehydrate, prerenderRoutes, useRequestHeader, useRequestHeaders, useResponseHeader, useRequestEvent, useRequestFetch, setResponseStatus } from '#app/composables/ssr'; +export { onNuxtReady } from '#app/composables/ready'; +export { preloadComponents, prefetchComponents, preloadRouteComponents } from '#app/composables/preload'; +export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter } from '#app/composables/router'; +export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from '#app/composables/payload'; +export { useLoadingIndicator } from '#app/composables/loading-indicator'; +export { getAppManifest, getRouteRules } from '#app/composables/manifest'; +export { reloadNuxtApp } from '#app/composables/chunk'; +export { useRequestURL } from '#app/composables/url'; +export { usePreviewMode } from '#app/composables/preview'; +export { useRouteAnnouncer } from '#app/composables/route-announcer'; +export { useAnnouncer } from '#app/composables/announcer'; +export { useRuntimeHook } from '#app/composables/runtime-hook'; +export { useHead, useHeadSafe, useServerHeadSafe, useServerHead, useSeoMeta, useServerSeoMeta, injectHead } from '#app/composables/head'; +export { onBeforeRouteLeave, onBeforeRouteUpdate, useLink } from 'vue-router'; +export { withCtx, withDirectives, withKeys, withMemo, withModifiers, withScopeId, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onServerPrefetch, onUnmounted, onUpdated, computed, customRef, isProxy, isReactive, isReadonly, isRef, markRaw, proxyRefs, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, watch, watchEffect, watchPostEffect, watchSyncEffect, onWatcherCleanup, isShallow, effect, effectScope, getCurrentScope, onScopeDispose, defineComponent, defineAsyncComponent, resolveComponent, getCurrentInstance, h, inject, hasInjectionContext, nextTick, provide, toValue, useModel, useAttrs, useCssModule, useCssVars, useSlots, useTransitionState, useId, useTemplateRef, useShadowRoot, Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'; +export { requestIdleCallback, cancelIdleCallback } from '#app/compat/idle-callback'; +export { setInterval } from '#app/compat/interval'; +export { computedAsync, asyncComputed, computedEager, eagerComputed, computedInject, computedWithControl, controlledComputed, createEventHook, createGlobalState, createInjectionState, createRef, createReusableTemplate, createSharedComposable, createTemplatePromise, createUnrefFn, extendRef, injectLocal, isDefined, makeDestructurable, onClickOutside, onElementRemoval, onKeyStroke, onLongPress, onStartTyping, provideLocal, reactify, createReactiveFn, reactifyObject, reactiveComputed, reactiveOmit, reactivePick, refAutoReset, autoResetRef, refDebounced, useDebounce, debouncedRef, refDefault, refManualReset, refThrottled, useThrottle, throttledRef, refWithControl, controlledRef, syncRef, syncRefs, templateRef, toReactive, tryOnBeforeMount, tryOnBeforeUnmount, tryOnMounted, tryOnScopeDispose, tryOnUnmounted, unrefElement, until, useActiveElement, useAnimate, useArrayDifference, useArrayEvery, useArrayFilter, useArrayFind, useArrayFindIndex, useArrayFindLast, useArrayIncludes, useArrayJoin, useArrayMap, useArrayReduce, useArraySome, useArrayUnique, useAsyncQueue, useAsyncState, useBase64, useBattery, useBluetooth, useBreakpoints, useBroadcastChannel, useBrowserLocation, useCached, useClipboard, useClipboardItems, useCloned, useConfirmDialog, useCountdown, useCounter, useCssSupports, useCssVar, useCurrentElement, useCycleList, useDark, useDateFormat, useDebouncedRefHistory, useDebounceFn, useDeviceMotion, useDeviceOrientation, useDevicePixelRatio, useDevicesList, useDisplayMedia, useDocumentVisibility, useDraggable, useDropZone, useElementBounding, useElementByPoint, useElementHover, useElementSize, useElementVisibility, useEventBus, useEventListener, useEventSource, useEyeDropper, useFavicon, useFileDialog, useFileSystemAccess, useFocus, useFocusWithin, useFps, useFullscreen, useGamepad, useGeolocation, useIdle, useInfiniteScroll, useIntersectionObserver, useInterval, useIntervalFn, useKeyModifier, useLastChanged, useLocalStorage, useMagicKeys, useManualRefHistory, useMediaControls, useMediaQuery, useMemoize, useMemory, useMounted, useMouse, useMouseInElement, useMousePressed, useMutationObserver, useNavigatorLanguage, useNetwork, useNow, useObjectUrl, useOffsetPagination, useOnline, usePageLeave, useParallax, useParentElement, usePerformanceObserver, usePermission, usePointer, usePointerLock, usePointerSwipe, usePreferredColorScheme, usePreferredContrast, usePreferredDark, usePreferredLanguages, usePreferredReducedMotion, usePreferredReducedTransparency, usePrevious, useRafFn, useRefHistory, useResizeObserver, useScreenOrientation, useScreenSafeArea, useScriptTag, useScroll, useScrollLock, useSessionStorage, useShare, useSorted, useSpeechRecognition, useSpeechSynthesis, useSSRWidth, useStepper, useStorageAsync, useStyleTag, useSupported, useSwipe, useTemplateRefsList, useTextareaAutosize, useTextDirection, useTextSelection, useThrottledRefHistory, useThrottleFn, useTimeAgo, useTimeAgoIntl, useTimeout, useTimeoutFn, useTimeoutPoll, useTimestamp, useTitle, useToggle, useToNumber, useToString, useTransition, useUrlSearchParams, useUserMedia, useVibrate, useVirtualList, useVModel, useVModels, useWakeLock, useWebNotification, useWebSocket, useWebWorker, useWebWorkerFn, useWindowFocus, useWindowScroll, useWindowSize, watchArray, watchAtMost, watchDebounced, debouncedWatch, watchDeep, watchIgnorable, ignorableWatch, watchImmediate, watchOnce, watchPausable, pausableWatch, watchThrottled, throttledWatch, watchTriggerable, watchWithFilter, whenever } from '@vueuse/core'; +export { definePageMeta, PageMeta } from '#app/composables/pages'; +export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration'; +export { colorMode, useDevtoolsConnection } from '../composables/rpc'; +export { state, isConnected, syncState, SeoOverview, SerializedEntry, SerializedTag, SerializedScript, SerializedValidationRule, UnheadDevtoolsState } from '../composables/state'; +export { SEO_LIMITS, estimatePixelWidth, titleColor, descColor } from '../composables/tools'; +export { defineLocale, extendLocale } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale'; +export { defineShortcuts, extractShortcuts } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts'; +export { useContentSearch } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch'; +export { useFileUpload } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload'; +export { useFormField } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useFormField'; +export { useKbd } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useKbd'; +export { useOverlay } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay'; +export { useResizable } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useResizable'; +export { useScrollShadow } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useScrollShadow'; +export { useScrollspy } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy'; +export { useToast } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useToast'; +export { useColorMode } from '../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/composables'; \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json b/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json b/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json b/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json b/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json b/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json b/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json b/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt-fonts-global.css b/packages/devtools-app/.nuxt/nuxt-fonts-global.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs b/packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs new file mode 100644 index 000000000..8f785f931 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs @@ -0,0 +1 @@ +export function init() {} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs b/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs new file mode 100644 index 000000000..95287ba80 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs @@ -0,0 +1,13 @@ +function createRemoteCollection(fetchEndpoint) { + let _cache + return async () => { + if (_cache) + return _cache + const res = await fetch(fetchEndpoint).then(r => r.json()) + _cache = res + return res + } +} + +export const collections = { +} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt.d.ts b/packages/devtools-app/.nuxt/nuxt.d.ts new file mode 100644 index 000000000..dfcccabfd --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt.d.ts @@ -0,0 +1,24 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +export {} diff --git a/packages/devtools-app/.nuxt/nuxt.node.d.ts b/packages/devtools-app/.nuxt/nuxt.node.d.ts new file mode 100644 index 000000000..ae6e2dec4 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt.node.d.ts @@ -0,0 +1,14 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +export {} diff --git a/packages/devtools-app/.nuxt/nuxt.shared.d.ts b/packages/devtools-app/.nuxt/nuxt.shared.d.ts new file mode 100644 index 000000000..812815770 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt.shared.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// +/// + +export {} diff --git a/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs b/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs new file mode 100644 index 000000000..f8de1bfd0 --- /dev/null +++ b/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs @@ -0,0 +1,19 @@ +import { escapeHtml } from 'file:///home/harlan/pkg/unhead/.claude/worktrees/feat-vite-devtools/node_modules/.pnpm/@vue+shared@3.5.32/node_modules/@vue/shared/dist/shared.cjs.prod.js'; + +const _messages = { + "appName": "Nuxt", + "status": 500, + "statusText": "Internal server error", + "description": "This page is temporarily unavailable.", + "refresh": "Refresh this page" +}; +const template = (messages) => { + messages = { + ..._messages, + ...messages + }; + return "" + escapeHtml(messages.status) + " - " + escapeHtml(messages.statusText) + " | " + escapeHtml(messages.appName) + " + + + + diff --git a/packages/devtools-app/assets/css/global.css b/packages/devtools-app/assets/css/global.css new file mode 100644 index 000000000..747deb497 --- /dev/null +++ b/packages/devtools-app/assets/css/global.css @@ -0,0 +1,27 @@ +@import "tailwindcss"; +@import "@nuxt/ui"; + +@theme { + --font-sans: 'Hubot Sans', ui-sans-serif, system-ui, sans-serif; + --font-mono: 'Fira Code', ui-monospace, monospace; +} + +html, body { + margin: 0; + padding: 0; + font-size: 14px; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-thumb { + background: var(--ui-border); + border-radius: 3px; +} + +::-webkit-scrollbar-track { + background: transparent; +} diff --git a/packages/devtools-app/composables/rpc.ts b/packages/devtools-app/composables/rpc.ts new file mode 100644 index 000000000..f2d13e7d5 --- /dev/null +++ b/packages/devtools-app/composables/rpc.ts @@ -0,0 +1,33 @@ +import type { UnheadDevtoolsState } from './state' +import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client' +import { isConnected, syncState } from './state' + +export const colorMode = ref<'dark' | 'light'>('dark') + +export async function useDevtoolsConnection(): Promise { + if (typeof window === 'undefined') + return + + console.log('[unhead devtools-app] useDevtoolsConnection: getting RPC client') + const client = await getDevToolsRpcClient() + console.log('[unhead devtools-app] RPC client obtained:', !!client) + + const sharedState = await (client.sharedState as any).get('unhead:state') + console.log('[unhead devtools-app] sharedState handle obtained') + + const current = sharedState.value() as UnheadDevtoolsState | null + console.log('[unhead devtools-app] initial value:', current ? `${current.tags.length} tags, ${current.entries.length} entries` : 'null') + + if (current) { + isConnected.value = true + syncState(current) + } + + sharedState.on('updated', (newState: UnheadDevtoolsState) => { + console.log('[unhead devtools-app] sharedState updated:', newState ? `${newState.tags.length} tags, ${newState.entries.length} entries` : 'null') + if (newState) { + isConnected.value = true + syncState(newState) + } + }) +} diff --git a/packages/devtools-app/composables/state.ts b/packages/devtools-app/composables/state.ts new file mode 100644 index 000000000..6bd7d032d --- /dev/null +++ b/packages/devtools-app/composables/state.ts @@ -0,0 +1,85 @@ +import { ref } from 'vue' + +export interface SeoOverview { + title: string + description: string + canonical: string + robots: string + ogTitle: string + ogDescription: string + ogImage: string +} + +export interface SerializedEntry { + id: number + source?: string + input: Record + tagCount: number +} + +export interface SerializedTag { + tag: string + props: Record + innerHTML?: string + textContent?: string + position?: string + priority?: number + dedupeKey?: string + source?: string +} + +export interface SerializedScript { + id: string + src: string + status: string +} + +export interface SerializedValidationRule { + id: string + message: string + severity: 'warn' | 'info' + source?: string +} + +export interface UnheadDevtoolsState { + entries: SerializedEntry[] + tags: SerializedTag[] + plugins: string[] + title: string + scripts: SerializedScript[] + seo: SeoOverview + titleTemplate: string | null + templateParams: Record | null + separator: string + ssr: boolean + dirty: boolean + domElementCount: number + tagTypeCounts: Record + validationRules: SerializedValidationRule[] +} + +const defaultState: UnheadDevtoolsState = { + entries: [], + tags: [], + plugins: [], + title: '', + scripts: [], + seo: { title: '', description: '', canonical: '', robots: '', ogTitle: '', ogDescription: '', ogImage: '' }, + titleTemplate: null, + templateParams: null, + separator: '|', + ssr: false, + dirty: false, + domElementCount: 0, + tagTypeCounts: {}, + validationRules: [], +} + +export const state = ref({ ...defaultState }) +export const isConnected = ref(false) + +export function syncState(newState: UnheadDevtoolsState) { + if (!newState) + return + state.value = newState +} diff --git a/packages/devtools-app/composables/tools.ts b/packages/devtools-app/composables/tools.ts new file mode 100644 index 000000000..bf419ab09 --- /dev/null +++ b/packages/devtools-app/composables/tools.ts @@ -0,0 +1,28 @@ +export const SEO_LIMITS = { + TITLE_MAX_CHARS: 60, + TITLE_WARN_CHARS: 50, + TITLE_MAX_PIXELS: 580, + DESC_MAX_CHARS: 160, + DESC_WARN_CHARS: 150, + DESC_MAX_PIXELS: 920, +} as const + +export function estimatePixelWidth(text: string, fontSize: number = 16): number { + return Math.round(text.length * fontSize * 0.55) +} + +export function titleColor(length: number) { + if (length > SEO_LIMITS.TITLE_MAX_CHARS) + return 'error' + if (length < 30) + return 'warning' + return 'success' +} + +export function descColor(length: number) { + if (length > SEO_LIMITS.DESC_MAX_CHARS) + return 'error' + if (length > SEO_LIMITS.DESC_WARN_CHARS || length < 70) + return 'warning' + return 'success' +} diff --git a/packages/devtools-app/nuxt.config.ts b/packages/devtools-app/nuxt.config.ts new file mode 100644 index 000000000..b4ecce47c --- /dev/null +++ b/packages/devtools-app/nuxt.config.ts @@ -0,0 +1,51 @@ +import { resolve } from 'pathe' + +export default defineNuxtConfig({ + ssr: false, + + modules: [ + '@nuxt/ui', + '@vueuse/nuxt', + ], + + css: [resolve(__dirname, 'assets/css/global.css')], + + imports: { + autoImport: true, + }, + + devtools: { + enabled: false, + }, + + compatibilityDate: '2026-03-13', + + nitro: { + prerender: { + crawlLinks: false, + routes: ['/'], + failOnError: false, + }, + output: { + publicDir: resolve(__dirname, 'dist'), + }, + }, + + vite: { + optimizeDeps: { + include: [ + '@vueuse/core', + ], + exclude: ['jiti'], + }, + resolve: { + alias: { + jiti: 'data:text/javascript,export default () => {}', + }, + }, + }, + + app: { + baseURL: '/__unhead/', + }, +}) diff --git a/packages/devtools-app/package.json b/packages/devtools-app/package.json new file mode 100644 index 000000000..36f14f422 --- /dev/null +++ b/packages/devtools-app/package.json @@ -0,0 +1,20 @@ +{ + "name": "@unhead/devtools-app", + "type": "module", + "private": true, + "scripts": { + "dev": "nuxi dev", + "build": "nuxi generate", + "generate": "nuxi generate" + }, + "devDependencies": { + "@nuxt/ui": "catalog:conflicts_@nuxt/ui_h4_6_1", + "@unhead/vue": "workspace:*", + "@vitejs/devtools-kit": "catalog:", + "@vueuse/nuxt": "catalog:conflicts_conflicts_@vueuse/nuxt_h14_h14_2_1", + "nuxt": "catalog:conflicts_nuxt_h4_4_2", + "pathe": "catalog:conflicts_pathe_h2", + "tailwindcss": "catalog:", + "vue": "catalog:" + } +} diff --git a/packages/devtools-app/pages/identity.vue b/packages/devtools-app/pages/identity.vue new file mode 100644 index 000000000..b40e199d4 --- /dev/null +++ b/packages/devtools-app/pages/identity.vue @@ -0,0 +1,475 @@ + + + + + diff --git a/packages/devtools-app/pages/index.vue b/packages/devtools-app/pages/index.vue new file mode 100644 index 000000000..641710234 --- /dev/null +++ b/packages/devtools-app/pages/index.vue @@ -0,0 +1,208 @@ + + + diff --git a/packages/devtools-app/pages/schema.vue b/packages/devtools-app/pages/schema.vue new file mode 100644 index 000000000..910af8d94 --- /dev/null +++ b/packages/devtools-app/pages/schema.vue @@ -0,0 +1,244 @@ + + + diff --git a/packages/devtools-app/pages/scripts.vue b/packages/devtools-app/pages/scripts.vue new file mode 100644 index 000000000..bebcea4f7 --- /dev/null +++ b/packages/devtools-app/pages/scripts.vue @@ -0,0 +1,106 @@ + + + diff --git a/packages/devtools-app/pages/serp.vue b/packages/devtools-app/pages/serp.vue new file mode 100644 index 000000000..e884d9bb7 --- /dev/null +++ b/packages/devtools-app/pages/serp.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/packages/devtools-app/tsconfig.json b/packages/devtools-app/tsconfig.json new file mode 100644 index 000000000..4b34df157 --- /dev/null +++ b/packages/devtools-app/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/packages/devtools-app/utils/schema-validation.ts b/packages/devtools-app/utils/schema-validation.ts new file mode 100644 index 000000000..168011666 --- /dev/null +++ b/packages/devtools-app/utils/schema-validation.ts @@ -0,0 +1,470 @@ +// Rich result schema types that Google supports +export const richResultTypes = new Set([ + 'Article', + 'NewsArticle', + 'BlogPosting', + 'ScholarlyArticle', + 'Product', + 'AggregateOffer', + 'Offer', + 'FAQPage', + 'Question', + 'HowTo', + 'HowToStep', + 'Recipe', + 'Event', + 'LocalBusiness', + 'Restaurant', + 'JobPosting', + 'Course', + 'Movie', + 'Book', + 'SoftwareApplication', + 'VideoObject', + 'Review', + 'AggregateRating', + 'BreadcrumbList', + 'SearchAction', + 'Dataset', + 'SpecialAnnouncement', + 'Person', + 'NewsMediaOrganization', + 'Organization', +]) + +// Google Rich Results requirements for each schema type +export const googleRichResultsRequirements: Record = { + Article: { + required: [], + recommended: ['author', 'author.name', 'dateModified', 'datePublished', 'headline', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/article', + }, + NewsArticle: { + required: [], + recommended: ['author', 'author.name', 'dateModified', 'datePublished', 'headline', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/article', + }, + BlogPosting: { + required: [], + recommended: ['author', 'author.name', 'dateModified', 'datePublished', 'headline', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/article', + }, + Product: { + required: ['name'], + recommended: ['description', 'offers', 'offers.price', 'offers.priceCurrency', 'offers.availability', 'aggregateRating', 'review', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/product', + }, + FAQPage: { + required: ['mainEntity'], + recommended: [], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/faqpage', + }, + Recipe: { + required: ['name', 'image'], + recommended: ['aggregateRating', 'author', 'cookTime', 'datePublished', 'description', 'keywords', 'nutrition', 'prepTime', 'recipeCategory', 'recipeCuisine', 'recipeIngredient', 'recipeInstructions', 'recipeYield', 'totalTime', 'video'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/recipe', + }, + Event: { + required: ['name', 'location', 'startDate'], + recommended: ['description', 'endDate', 'eventStatus', 'image', 'offers', 'performer', 'organizer'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/event', + }, + LocalBusiness: { + required: ['name', 'address'], + recommended: ['aggregateRating', 'department', 'geo', 'openingHoursSpecification', 'priceRange', 'telephone', 'url'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/local-business', + }, + Restaurant: { + required: ['name', 'address'], + recommended: ['aggregateRating', 'servesCuisine', 'hasMenu', 'geo', 'openingHoursSpecification', 'priceRange', 'telephone', 'url'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/local-business', + }, + Review: { + required: ['author', 'itemReviewed', 'reviewRating'], + recommended: ['datePublished', 'reviewRating.bestRating', 'reviewRating.worstRating'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/review-snippet', + }, + AggregateRating: { + required: ['itemReviewed', 'ratingValue'], + recommended: ['bestRating', 'worstRating', 'ratingCount', 'reviewCount'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/review-snippet', + }, + BreadcrumbList: { + required: ['itemListElement'], + recommended: [], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/breadcrumb', + }, + Organization: { + required: [], + recommended: ['name', 'logo', 'url', 'email', 'telephone', 'contactPoint', 'sameAs', 'address', 'description'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/organization', + }, + Person: { + required: [], + recommended: ['name', 'url', 'image', 'sameAs', 'jobTitle', 'worksFor'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/person', + }, + SoftwareApplication: { + required: ['name', 'offers'], + recommended: ['applicationCategory', 'operatingSystem', 'aggregateRating', 'review'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/software-app', + }, + VideoObject: { + required: ['name', 'thumbnailUrl', 'uploadDate'], + recommended: ['contentUrl', 'description', 'duration', 'embedUrl'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/video', + }, + JobPosting: { + required: ['datePosted', 'description', 'hiringOrganization', 'jobLocation', 'title'], + recommended: ['applicantLocationRequirements', 'baseSalary', 'employmentType', 'jobLocationType', 'validThrough'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/job-posting', + }, + Course: { + required: ['description', 'name'], + recommended: ['provider'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/course', + }, +} + +// Recursively extract all typed objects from a schema node +function extractNestedTypes(obj: any, nodes: any[], seen: Set): void { + if (!obj || typeof obj !== 'object' || seen.has(obj)) + return + seen.add(obj) + + if (obj['@type'] && Object.keys(obj).length > 1) { + nodes.push(obj) + } + + for (const value of Object.values(obj)) { + if (Array.isArray(value)) { + for (const item of value) { + extractNestedTypes(item, nodes, seen) + } + } + else if (typeof value === 'object') { + extractNestedTypes(value, nodes, seen) + } + } +} + +// Resolve @id references in a node +function resolveNodeReferences(node: any, nodeMap: Map): any { + const resolved = { ...node } + + Object.keys(resolved).forEach((key) => { + const value = resolved[key] + + if ( + value + && typeof value === 'object' + && !Array.isArray(value) + && Object.keys(value).length === 1 + && value['@id'] + && nodeMap.has(value['@id']) + ) { + resolved[key] = { ...nodeMap.get(value['@id']) } + } + else if (Array.isArray(value)) { + resolved[key] = value.map((item: any) => { + if ( + item + && typeof item === 'object' + && Object.keys(item).length === 1 + && item['@id'] + && nodeMap.has(item['@id']) + ) { + return { ...nodeMap.get(item['@id']) } + } + return item + }) + } + else if (value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 1) { + resolved[key] = resolveNodeReferences(value, nodeMap) + } + }) + + return resolved +} + +// Extract all nodes from a schema graph (including nested @graph and nested types) +export function extractSchemaNodes(data: any): any[] { + const nodes: any[] = [] + if (!data) + return nodes + + const seen = new Set() + + if (data['@graph'] && Array.isArray(data['@graph'])) { + const nodeMap = new Map() + data['@graph'].forEach((node: any) => { + if (node && typeof node === 'object' && node['@id']) { + nodeMap.set(node['@id'], node) + } + }) + + const addedIds = new Set() + + data['@graph'].forEach((node: any) => { + if (node && typeof node === 'object') { + if (Object.keys(node).length === 1 && node['@id']) + return + if (node['@id'] && addedIds.has(node['@id'])) + return + + const resolvedNode = resolveNodeReferences(node, nodeMap) + extractNestedTypes(resolvedNode, nodes, seen) + + if (node['@id']) + addedIds.add(node['@id']) + } + }) + } + else if (data['@type']) { + extractNestedTypes(data, nodes, seen) + } + + // Deduplicate nodes that appear multiple times due to @id reference resolution + const uniqueNodes: any[] = [] + const seenIds = new Set() + for (const node of nodes) { + const id = node['@id'] + if (id) { + if (seenIds.has(id)) + continue + seenIds.add(id) + } + uniqueNodes.push(node) + } + + return uniqueNodes +} + +export function getNodeType(node: any): string { + if (!node || !node['@type']) + return 'Unknown' + return Array.isArray(node['@type']) ? node['@type'][0] : node['@type'] +} + +export function isRichResultType(type: string): boolean { + return richResultTypes.has(type) +} + +export function getNodeDescription(node: any): string { + const type = getNodeType(node) + + if (node.name) + return node.name + if (node.headline) + return node.headline + if (node.title) + return node.title + if (node.description) { + return typeof node.description === 'string' + ? node.description.substring(0, 100) + (node.description.length > 100 ? '...' : '') + : '' + } + if (node['@id']) + return node['@id'] + if (node.url) + return node.url + + return `${type} Schema` +} + +export function getNestedProperty(obj: any, path: string): any { + const parts = path.split('.') + let current = obj + + for (const part of parts) { + if (current && typeof current === 'object' && part in current) { + current = current[part] + } + else { + return undefined + } + } + + return current +} + +export function analyzeNodeProperties(node: any): { + missingRequired: string[] + missingRecommended: string[] + presentProperties: Record +} { + const type = getNodeType(node) + const requirements = googleRichResultsRequirements[type] + + if (!requirements) { + return { + missingRequired: [], + missingRecommended: [], + presentProperties: {}, + } + } + + const missingRequired: string[] = [] + const missingRecommended: string[] = [] + const presentProperties: Record = {} + + requirements.required.forEach((prop) => { + const value = getNestedProperty(node, prop) + if (value === undefined || value === null || value === '') { + missingRequired.push(prop) + } + else { + presentProperties[prop] = value + } + }) + + requirements.recommended.forEach((prop) => { + const value = getNestedProperty(node, prop) + if (value === undefined || value === null || value === '') { + missingRecommended.push(prop) + } + else { + presentProperties[prop] = value + } + }) + + return { missingRequired, missingRecommended, presentProperties } +} + +export function formatPropertyValue(value: any): string { + if (value === null || value === undefined) + return 'null' + if (typeof value === 'string') + return value.length > 50 ? `${value.substring(0, 50)}...` : value + if (typeof value === 'number') + return value.toString() + if (typeof value === 'boolean') + return value ? 'true' : 'false' + if (Array.isArray(value)) + return `[${value.length} items]` + if (typeof value === 'object' && value['@type']) + return value['@type'] + if (typeof value === 'object') + return '{...}' + return String(value) +} + +export function getSchemaIcon(type: string): string { + const iconMap: Record = { + Article: 'carbon:document', + NewsArticle: 'carbon:news', + BlogPosting: 'carbon:blog', + Product: 'carbon:shopping-cart', + FAQPage: 'carbon:help', + Organization: 'carbon:building', + LocalBusiness: 'carbon:location', + Person: 'carbon:user', + Event: 'carbon:calendar', + SoftwareApplication: 'carbon:application', + Recipe: 'carbon:restaurant', + HowTo: 'carbon:list-numbered', + WebSite: 'carbon:earth', + WebPage: 'carbon:page-first', + BreadcrumbList: 'carbon:flow', + VideoObject: 'carbon:video', + Review: 'carbon:star', + AggregateRating: 'carbon:star-filled', + SearchAction: 'carbon:search', + } + return iconMap[type] || 'carbon:code' +} + +export interface ValidationSummary { + totalNodes: number + richResultNodes: number + totalErrors: number + totalWarnings: number +} + +// Google structured data page slugs mapped to schema types +export const googleStructuredDataLinks: Record = { + 'article': ['Article', 'NewsArticle', 'BlogPosting'], + 'book': ['Book'], + 'breadcrumb': ['BreadcrumbList'], + 'carousel': ['ItemList'], + 'course-info': ['Course'], + 'course': ['Course'], + 'dataset': ['Dataset'], + 'discussion-forum': ['DiscussionForumPosting'], + 'education-qa': ['Question', 'Answer'], + 'employer-rating': ['EmployerAggregateRating'], + 'estimated-salary': ['OccupationalExperienceRequirements'], + 'event': ['Event'], + 'factcheck': ['ClaimReview'], + 'faqpage': ['FAQPage'], + 'image-license-metadata': ['ImageObject'], + 'job-posting': ['JobPosting'], + 'learning-video': ['LearningResource', 'VideoObject'], + 'local-business': ['LocalBusiness'], + 'math-solvers': ['MathSolver'], + 'movie': ['Movie'], + 'organization': ['Organization'], + 'practice-problems': ['Quiz', 'Question'], + 'product': ['Product'], + 'product-snippet': ['Product'], + 'merchant-listing': ['Product', 'Offer'], + 'product-variants': ['Product'], + 'profile-page': ['ProfilePage', 'Person'], + 'qapage': ['QAPage'], + 'recipe': ['Recipe'], + 'review-snippet': ['Review'], + 'software-app': ['SoftwareApplication'], + 'speakable': ['SpeakableSpecification'], + 'special-announcements': ['SpecialAnnouncement'], + 'paywalled-content': ['CreativeWork'], + 'vacation-rental': ['Accommodation', 'LodgingBusiness'], + 'vehicle-listing': ['Vehicle'], + 'video': ['VideoObject'], +} + +export function nodeToSchemaOrgLink(type: string) { + const simpleType = type.replace('https://schema.org/', '') + const googlePage = Object.entries(googleStructuredDataLinks) + .find(([_, types]) => types.includes(simpleType))?.[0] + return { + type: simpleType, + schemaOrg: `https://schema.org/${simpleType}`, + googlePage: googlePage ? `https://developers.google.com/search/docs/appearance/structured-data/${googlePage}` : null, + } +} + +export function asArray(value: T | T[]): T[] { + return Array.isArray(value) ? value : [value] +} + +export function validateGraph(data: any): { nodes: any[], summary: ValidationSummary } { + const nodes = extractSchemaNodes(data) + + let totalErrors = 0 + let totalWarnings = 0 + let richResultNodes = 0 + + for (const node of nodes) { + const type = getNodeType(node) + if (isRichResultType(type)) + richResultNodes++ + + const analysis = analyzeNodeProperties(node) + totalErrors += analysis.missingRequired.length + totalWarnings += analysis.missingRecommended.length + } + + return { + nodes, + summary: { + totalNodes: nodes.length, + richResultNodes, + totalErrors, + totalWarnings, + }, + } +} diff --git a/packages/devtools/build.config.ts b/packages/devtools/build.config.ts new file mode 100644 index 000000000..9cb67a34f --- /dev/null +++ b/packages/devtools/build.config.ts @@ -0,0 +1,18 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + clean: true, + declaration: true, + entries: [ + { input: 'src/index', name: 'index' }, + { input: 'src/vite', name: 'vite' }, + { input: 'src/plugin', name: 'plugin' }, + { input: 'src/bridge', name: 'bridge' }, + ], + externals: [ + 'vite', + 'unhead', + '@vitejs/devtools-kit', + '@vitejs/devtools-kit/client', + ], +}) diff --git a/packages/devtools/package.json b/packages/devtools/package.json new file mode 100644 index 000000000..4c66e2cb8 --- /dev/null +++ b/packages/devtools/package.json @@ -0,0 +1,70 @@ +{ + "name": "@unhead/devtools", + "type": "module", + "version": "3.0.0-beta.12", + "description": "Vite DevTools integration for Unhead.", + "author": "Harlan Wilton ", + "license": "MIT", + "funding": "https://github.com/sponsors/harlan-zw", + "homepage": "https://unhead.unjs.io", + "repository": { + "type": "git", + "url": "git+https://github.com/unjs/unhead.git", + "directory": "packages/devtools" + }, + "publishConfig": { + "access": "public", + "tag": "next" + }, + "bugs": { + "url": "https://github.com/unjs/unhead/issues" + }, + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "./vite": { + "types": "./dist/vite.d.ts", + "default": "./dist/vite.mjs" + }, + "./plugin": { + "types": "./dist/plugin.d.ts", + "default": "./dist/plugin.mjs" + }, + "./bridge": { + "types": "./dist/bridge.d.ts", + "default": "./dist/bridge.mjs" + } + }, + "main": "dist/index.mjs", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "unbuild", + "stub": "unbuild --stub" + }, + "peerDependencies": { + "vite": ">=6.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + }, + "dependencies": { + "magic-string": "catalog:", + "oxc-parser": "catalog:", + "oxc-walker": "catalog:", + "unhead": "workspace:*" + }, + "devDependencies": { + "@vitejs/devtools-kit": "catalog:", + "unbuild": "catalog:", + "vite": "catalog:" + } +} diff --git a/packages/devtools/src/bridge.ts b/packages/devtools/src/bridge.ts new file mode 100644 index 000000000..df30d887a --- /dev/null +++ b/packages/devtools/src/bridge.ts @@ -0,0 +1,240 @@ +import type { SeoOverview, SerializedScript, SerializedTag, SerializedValidationRule, UnheadDevtoolsState } from './rpc/types' + +declare global { + interface Window { + __unhead__?: { + _head?: any + _q?: any[] + push?: (e: any) => void + } + __unhead_devtools__?: any + } +} + +function extractSeoOverview(tags: SerializedTag[], title: string): SeoOverview { + const seo: SeoOverview = { + title, + description: '', + canonical: '', + robots: '', + ogTitle: '', + ogDescription: '', + ogImage: '', + } + for (const t of tags) { + if (t.tag === 'meta') { + const name = t.props.name + const property = t.props.property + const content = t.props.content || '' + if (name === 'description') + seo.description = content + else if (name === 'robots') + seo.robots = content + else if (property === 'og:title') + seo.ogTitle = content + else if (property === 'og:description') + seo.ogDescription = content + else if (property === 'og:image') + seo.ogImage = content + } + else if (t.tag === 'link' && t.props.rel === 'canonical') { + seo.canonical = t.props.href || '' + } + } + return seo +} + +function serializeHeadState(head: any): UnheadDevtoolsState { + const entries: UnheadDevtoolsState['entries'] = [] + const allTags: UnheadDevtoolsState['tags'] = [] + const tagTypeCounts: Record = {} + + if (head.entries) { + for (const [id, entry] of head.entries) { + const tags = entry._tags || [] + entries.push({ + id, + source: entry.options?._source, + input: JSON.parse(JSON.stringify(entry.input || {})), + tagCount: tags.length, + }) + for (const tag of tags) { + const tagName = tag.tag + tagTypeCounts[tagName] = (tagTypeCounts[tagName] || 0) + 1 + allTags.push({ + tag: tagName, + props: { ...tag.props }, + innerHTML: tag.innerHTML, + textContent: tag.textContent, + position: tag.tagPosition, + priority: tag._w, + dedupeKey: tag._d, + source: tag._source || entry.options?._source, + }) + } + } + } + + const plugins: string[] = [] + if (head.plugins) { + for (const [key] of head.plugins) { + plugins.push(key) + } + } + + const scripts: SerializedScript[] = [] + if (head._scripts) { + for (const [id, script] of Object.entries(head._scripts)) { + const s = script as any + scripts.push({ + id, + src: s.src || s.input?.src || '', + status: s.status || 'unknown', + }) + } + } + + let templateParams: Record | null = null + if (head._templateParams) { + try { + templateParams = JSON.parse(JSON.stringify(head._templateParams)) + } + catch {} + } + + const title = head._title || document.title || '' + + // Read validation rules stored by ValidatePlugin (if active) + const validationRules: SerializedValidationRule[] = (head._validationRules || []).map((r: any) => ({ + id: r.id, + message: r.message, + severity: r.severity, + source: r.source, + })) + + return { + entries, + tags: allTags, + plugins, + title, + scripts, + seo: extractSeoOverview(allTags, title), + titleTemplate: head._titleTemplate + ? (typeof head._titleTemplate === 'function' ? String(head._titleTemplate) : head._titleTemplate) + : null, + templateParams, + separator: head._separator || '|', + ssr: !!head.ssr, + dirty: !!head.dirty, + domElementCount: head._dom?._e?.size || 0, + tagTypeCounts, + validationRules, + } +} + +function connectBridge(head: any) { + let sharedState: any + + function syncToSharedState() { + if (!sharedState) { + console.log('[unhead bridge] syncToSharedState: no sharedState yet') + return + } + const newState = serializeHeadState(head) + console.log('[unhead bridge] syncing state:', newState.tags.length, 'tags,', newState.entries.length, 'entries') + sharedState.mutate((draft: any) => { + Object.assign(draft, newState) + }) + } + + async function init() { + console.log('[unhead bridge] init: importing devtools-kit/client') + const { getDevToolsClientContext } = await import('@vitejs/devtools-kit/client') + + // Retry until DevTools client context is available + let ctx = getDevToolsClientContext() + console.log('[unhead bridge] getDevToolsClientContext:', ctx ? 'found' : 'not ready') + if (!ctx) { + let retries = 0 + await new Promise((resolve) => { + const timer = globalThis.setInterval(() => { + ctx = getDevToolsClientContext() + if (ctx || ++retries > 50) { + globalThis.clearInterval(timer) + if (ctx) + console.log('[unhead bridge] context found after', retries, 'retries') + else console.warn('[unhead bridge] gave up waiting for DevTools context after 50 retries') + resolve() + } + }, 100) + }) + } + if (!ctx) { + console.warn('[unhead bridge] no DevTools client context, aborting') + return + } + + console.log('[unhead bridge] creating shared state') + sharedState = await ctx.rpc.sharedState.get('unhead:state', { + initialValue: serializeHeadState(head), + }) + console.log('[unhead bridge] shared state created, initial value set') + + if (head.hooks) { + head.hooks.hook('dom:rendered', syncToSharedState) + console.log('[unhead bridge] hooked dom:rendered') + } + + // Initial sync after a short delay to capture early entries + setTimeout(syncToSharedState, 500) + } + + init().catch((err) => { + console.error('[unhead bridge] init failed:', err) + }) +} + +function findHead(): any { + // 1. Streaming plugin pattern: window.__unhead__._head + if (window.__unhead__?._head) { + console.log('[unhead bridge] found head via __unhead__._head') + return window.__unhead__._head + } + + // 2. Devtools-exposed pattern: window.__unhead_devtools__ + if (window.__unhead_devtools__) { + console.log('[unhead bridge] found head via __unhead_devtools__') + return window.__unhead_devtools__ + } + + return null +} + +function pollForHead() { + console.log('[unhead bridge] polling for head instance...') + let attempts = 0 + const handle = globalThis.setInterval(() => { + const h = findHead() + if (h) { + globalThis.clearInterval(handle) + console.log('[unhead bridge] head found after', attempts, 'attempts') + connectBridge(h) + } + if (++attempts > 50) { + globalThis.clearInterval(handle) + console.warn('[unhead bridge] gave up polling for head after 50 attempts') + } + }, 100) +} + +console.log('[unhead bridge] bridge script loaded') +if (typeof window !== 'undefined') { + const head = findHead() + if (head) { + console.log('[unhead bridge] head found immediately') + connectBridge(head) + } + else { + pollForHead() + } +} diff --git a/packages/devtools/src/index.ts b/packages/devtools/src/index.ts new file mode 100644 index 000000000..bedf9f855 --- /dev/null +++ b/packages/devtools/src/index.ts @@ -0,0 +1,2 @@ +export { devtoolsPlugin } from './plugin' +export type { SeoOverview, SerializedEntry, SerializedScript, SerializedTag, SerializedValidationRule, UnheadDevtoolsState } from './rpc/types' diff --git a/packages/devtools/src/plugin.ts b/packages/devtools/src/plugin.ts new file mode 100644 index 000000000..10b212ff6 --- /dev/null +++ b/packages/devtools/src/plugin.ts @@ -0,0 +1,26 @@ +import type { HeadPluginInput } from 'unhead' + +/** + * Unhead plugin that propagates source location metadata from entry options to tags. + * Used in dev mode for devtools source tracing. + * + * When the Vite transform injects `_source` into `useHead()` options, this plugin + * ensures that metadata flows through to each resolved tag so the devtools can + * display which file:line created each tag. + */ +export function devtoolsPlugin(): HeadPluginInput { + return { + key: 'devtools', + hooks: { + 'entries:normalize': function ({ tags, entry }) { + const source = entry.options?._source + if (!source) + return + for (const tag of tags) { + if (!tag._source) + tag._source = source + } + }, + }, + } +} diff --git a/packages/devtools/src/rpc/functions/get-config.ts b/packages/devtools/src/rpc/functions/get-config.ts new file mode 100644 index 000000000..c01c445f1 --- /dev/null +++ b/packages/devtools/src/rpc/functions/get-config.ts @@ -0,0 +1,12 @@ +import { defineRpcFunction } from '@vitejs/devtools-kit' + +export const getConfigRpc = defineRpcFunction({ + name: 'unhead:get-config', + type: 'static', + setup: ctx => ({ + handler: () => ({ + cwd: ctx.cwd, + mode: ctx.mode, + }), + }), +}) diff --git a/packages/devtools/src/rpc/index.ts b/packages/devtools/src/rpc/index.ts new file mode 100644 index 000000000..581c788d1 --- /dev/null +++ b/packages/devtools/src/rpc/index.ts @@ -0,0 +1 @@ +export { getConfigRpc } from './functions/get-config' diff --git a/packages/devtools/src/rpc/types.ts b/packages/devtools/src/rpc/types.ts new file mode 100644 index 000000000..3e62d707d --- /dev/null +++ b/packages/devtools/src/rpc/types.ts @@ -0,0 +1,57 @@ +export interface SerializedEntry { + id: number + source?: string + input: Record + tagCount: number +} + +export interface SerializedTag { + tag: string + props: Record + innerHTML?: string + textContent?: string + position?: string + priority?: number + dedupeKey?: string + source?: string +} + +export interface SerializedScript { + id: string + src: string + status: string +} + +export interface SeoOverview { + title: string + description: string + canonical: string + robots: string + ogTitle: string + ogDescription: string + ogImage: string +} + +export interface SerializedValidationRule { + id: string + message: string + severity: 'warn' | 'info' + source?: string +} + +export interface UnheadDevtoolsState { + entries: SerializedEntry[] + tags: SerializedTag[] + plugins: string[] + title: string + scripts: SerializedScript[] + seo: SeoOverview + titleTemplate: string | null + templateParams: Record | null + separator: string + ssr: boolean + dirty: boolean + domElementCount: number + tagTypeCounts: Record + validationRules: SerializedValidationRule[] +} diff --git a/packages/devtools/src/vite.ts b/packages/devtools/src/vite.ts new file mode 100644 index 000000000..8a54ab3e0 --- /dev/null +++ b/packages/devtools/src/vite.ts @@ -0,0 +1,169 @@ +/// +import type { Plugin } from 'vite' +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import MagicString from 'magic-string' +import { parseAndWalk } from 'oxc-walker' +import { getConfigRpc } from './rpc' + +const HEAD_COMPOSABLES = ['useHead', 'useSeoMeta', 'useHeadSafe', 'useScript'] +const HEAD_FACTORIES = ['createHead'] +const FILE_RE = /\.(vue|tsx?|jsx?|svelte)$/ + +const UNHEAD_ICON = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%2300dc82' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39'/%3E%3C/svg%3E` + +const DEVTOOLS_UI_ROUTE = '/__unhead/' + +function hasHeadCode(code: string): boolean { + return HEAD_COMPOSABLES.some(c => code.includes(c)) || HEAD_FACTORIES.some(c => code.includes(c)) +} + +/** + * Transforms source code to inject `_source` metadata into head composable calls. + */ +function transformSourceLocations(code: string, id: string, root: string): { code: string, map: any } | undefined { + if (!hasHeadCode(code)) + return + + const s = new MagicString(code) + let transformed = false + + const relativePath = id.startsWith(root) + ? id.slice(root.length).replace(/^\//, '') + : id + + parseAndWalk(code, id, { + parseOptions: { lang: 'ts' }, + enter(node: any) { + if (node.type !== 'CallExpression') + return + const callee = node.callee + if (!callee) + return + + const name = callee.type === 'Identifier' + ? callee.name + : callee.type === 'MemberExpression' && callee.property?.type === 'Identifier' + ? callee.property.name + : null + + // Detect createHead() and wrap to expose on window for the bridge + if (name && HEAD_FACTORIES.includes(name)) { + s.prependLeft(node.start, `((_h)=>(typeof window!=='undefined'&&(window.__unhead_devtools__=_h),_h))(`) + s.appendRight(node.end, `)`) + transformed = true + return + } + + if (!name || !HEAD_COMPOSABLES.includes(name)) + return + + const args = node.arguments + if (!args || args.length === 0) + return + + const lineNumber = code.slice(0, node.start).split('\n').length + const sourceValue = `${relativePath}:${lineNumber}` + + if (args.length === 1) { + const argEnd = args[0].end + s.appendRight(argEnd, `, { _source: ${JSON.stringify(sourceValue)} }`) + transformed = true + } + else if (args.length >= 2 && args[1].type === 'ObjectExpression') { + const objStart = args[1].start + 1 + s.appendRight(objStart, ` _source: ${JSON.stringify(sourceValue)},`) + transformed = true + } + }, + }) + + if (!transformed) + return + + return { + code: s.toString(), + map: s.generateMap({ includeContent: true, source: id }), + } +} + +export function unheadDevtools(): Plugin { + let root = '' + let bridgeCode: string | undefined + const pkgDir = fileURLToPath(new URL('..', import.meta.url)) + + return { + name: '@unhead/devtools', + apply: 'serve', + + configResolved(config) { + root = config.root + const bridgePath = resolve(pkgDir, 'dist/bridge.mjs') + if (existsSync(bridgePath)) + bridgeCode = readFileSync(bridgePath, 'utf-8') + }, + + configureServer(server) { + // Bridge middleware + server.middlewares.use('/@unhead/bridge.mjs', async (_req, res) => { + const result = await server.transformRequest('/@unhead/bridge.mjs') + res.setHeader('Content-Type', 'application/javascript') + res.end(result?.code || 'console.warn("[unhead devtools] bridge not built")') + }) + }, + + resolveId(id) { + if (id === '/@unhead/bridge.mjs') + return id + }, + + load(id) { + if (id !== '/@unhead/bridge.mjs') + return + if (!bridgeCode) + return 'console.warn("[unhead devtools] bridge not built")' + const kitClientPath = resolve(pkgDir, 'node_modules/@vitejs/devtools-kit/dist/client.js') + if (existsSync(kitClientPath)) + return bridgeCode.replace(`'@vitejs/devtools-kit/client'`, `'${kitClientPath}'`) + return bridgeCode + }, + + transform: { + filter: { id: FILE_RE }, + handler(code, id) { + return transformSourceLocations(code, id, root) + }, + }, + + transformIndexHtml() { + return [{ + tag: 'script', + attrs: { type: 'module' }, + children: `import("/@unhead/bridge.mjs")`, + injectTo: 'body-close', + }] + }, + + devtools: { + setup(ctx) { + const clientPath = resolve(pkgDir, '../devtools-app/dist') + if (existsSync(clientPath)) { + ctx.views.hostStatic(DEVTOOLS_UI_ROUTE, clientPath) + } + + ctx.docks.register({ + id: 'unhead', + title: 'Unhead', + icon: UNHEAD_ICON, + type: 'iframe', + url: DEVTOOLS_UI_ROUTE, + }) + + ctx.rpc.register(getConfigRpc) + }, + }, + } +} + +export default unheadDevtools diff --git a/packages/devtools/tsconfig.json b/packages/devtools/tsconfig.json new file mode 100644 index 000000000..75eba9f99 --- /dev/null +++ b/packages/devtools/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/unhead/src/plugins/validate.ts b/packages/unhead/src/plugins/validate.ts index 3b36ab93d..2094cc52f 100644 --- a/packages/unhead/src/plugins/validate.ts +++ b/packages/unhead/src/plugins/validate.ts @@ -705,6 +705,9 @@ export function ValidatePlugin(options: ValidatePluginOptions = {}) { } } + // Store rules on the head instance for devtools integration + ;(head as any)._validationRules = rules + // Dispatch if (rules.length) { if (options.onReport) { diff --git a/packages/unhead/src/types/head.ts b/packages/unhead/src/types/head.ts index 833268df9..237ca73d0 100644 --- a/packages/unhead/src/types/head.ts +++ b/packages/unhead/src/types/head.ts @@ -200,6 +200,11 @@ export interface HeadEntryOptions extends TagPosition, TagPriority, ProcessesTem * @internal */ _index?: number + /** + * Source location for devtools tracing. + * @internal + */ + _source?: string } export type HeadRenderer = (head: Unhead) => T diff --git a/packages/unhead/src/types/tags.ts b/packages/unhead/src/types/tags.ts index 91f89e562..7f2d13440 100644 --- a/packages/unhead/src/types/tags.ts +++ b/packages/unhead/src/types/tags.ts @@ -115,6 +115,11 @@ export interface HeadTag extends TagPriority, TagPosition, ResolvesDuplicates, H * @internal */ _h?: string + /** + * Source file:line that created this tag (devtools only). + * @internal + */ + _source?: string } export type HeadTagKeys = (keyof HeadTag)[] diff --git a/packages/vue/src/composables.ts b/packages/vue/src/composables.ts index bbb01bfaf..f9547d829 100644 --- a/packages/vue/src/composables.ts +++ b/packages/vue/src/composables.ts @@ -96,3 +96,10 @@ export function useSeoMeta(input: UseSeoMetaInput = {}, options: UseHeadOptions } export { useScript } from './scripts/useScript' + +/** @deprecated Use `useHead` instead. */ +export const useServerHead = useHead +/** @deprecated Use `useHeadSafe` instead. */ +export const useServerHeadSafe = useHeadSafe +/** @deprecated Use `useSeoMeta` instead. */ +export const useServerSeoMeta = useSeoMeta diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 9c1781ed9..940701f89 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -1,7 +1,8 @@ export { unheadVueComposablesImports } from './autoImports' -export { injectHead, useHead, useHeadSafe, useScript, useSeoMeta } from './composables' +export { injectHead, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta } from './composables' export { headSymbol } from './install' export type * from './scripts/index' export type * from './types' +export { resolveUnrefHeadInput } from './utils' export { VueHeadMixin } from './VueHeadMixin' export { createUnhead } from 'unhead' diff --git a/packages/vue/src/utils.ts b/packages/vue/src/utils.ts index d29470c9b..9f6bdbede 100644 --- a/packages/vue/src/utils.ts +++ b/packages/vue/src/utils.ts @@ -1,2 +1,13 @@ -export { VueResolver } from './resolver' +import type { ResolvedHead } from 'unhead/types' +import { walkResolver } from 'unhead/utils' +import { VueResolver } from './resolver' + +export { VueResolver } export * from 'unhead/utils' + +/** + * @deprecated Use head.resolveTags() instead. + */ +export function resolveUnrefHeadInput(input: any): ResolvedHead { + return walkResolver(input, VueResolver) +} From d49e036fc38c1227e86301ef3062baa4e47b2ade Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 8 Apr 2026 12:40:10 +1000 Subject: [PATCH 2/7] feat: vite devtools integration Add devtools and devtools-app packages with Vue composables and plugin updates. --- examples/vite-ssr-vue/vite.config.ts | 6 +- packages/devtools-app/.nuxt/app.config.mjs | 317 ++++ ...aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff | 0 ...R_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff | 0 ...you9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff | 0 ...gqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff | 0 ...O5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff | 0 ...l5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff | 0 ..._cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff | 0 ...9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff | 0 ...a0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff | 0 ...ZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff | 0 ...6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff | 0 ...Z24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff | 0 ...nTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 | 0 ...HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 | 0 ...YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 | 0 ...PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 | 0 ...3svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 | 0 ...z3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 | 0 ...L_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 | 0 ...Vrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 | 0 ...hd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 | 0 ...eLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 | 0 ...O3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 | 0 ...QdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 | 0 ...lNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 | 0 packages/devtools-app/.nuxt/components.d.ts | 322 ++++ packages/devtools-app/.nuxt/imports.d.ts | 49 + .../096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json | 1 + .../0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json | 1 + .../381b0558-98b7-448d-9168-38957e42e433.json | 1 + .../4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json | 1 + .../8251125c-23c7-4428-a540-3c662e362e93.json | 1 + .../c2629906-49d7-48f6-8234-c88fca0b6c44.json | 1 + .../cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json | 1 + .../devtools-app/.nuxt/nuxt-fonts-global.css | 0 .../.nuxt/nuxt-icon-client-bundle.mjs | 1 + .../.nuxt/nuxt-icon-server-bundle.mjs | 13 + packages/devtools-app/.nuxt/nuxt.d.ts | 24 + packages/devtools-app/.nuxt/nuxt.node.d.ts | 14 + packages/devtools-app/.nuxt/nuxt.shared.d.ts | 6 + .../.nuxt/prerender/chunks/_/error-500.mjs | 19 + .../prerender/chunks/_/error-500.mjs.map | 1 + .../.nuxt/prerender/chunks/_/renderer.mjs | 425 +++++ .../.nuxt/prerender/chunks/_/renderer.mjs.map | 1 + .../chunks/build/client.precomputed.mjs | 4 + .../chunks/build/client.precomputed.mjs.map | 1 + .../.nuxt/prerender/chunks/nitro/nitro.mjs | 1596 +++++++++++++++++ .../prerender/chunks/nitro/nitro.mjs.map | 1 + .../chunks/virtual/_virtual_spa-template.mjs | 4 + .../virtual/_virtual_spa-template.mjs.map | 1 + .../devtools-app/.nuxt/prerender/index.mjs | 24 + .../.nuxt/prerender/index.mjs.map | 1 + .../.nuxt/schema/nuxt.schema.d.ts | 210 +++ .../.nuxt/schema/nuxt.schema.json | 263 +++ packages/devtools-app/.nuxt/tsconfig.app.json | 232 +++ packages/devtools-app/.nuxt/tsconfig.json | 235 +++ .../devtools-app/.nuxt/tsconfig.node.json | 127 ++ .../devtools-app/.nuxt/tsconfig.server.json | 164 ++ .../devtools-app/.nuxt/tsconfig.shared.json | 164 ++ .../devtools-app/.nuxt/types/app.config.d.ts | 331 ++++ packages/devtools-app/.nuxt/types/build.d.ts | 25 + .../devtools-app/.nuxt/types/builder-env.d.ts | 1 + .../devtools-app/.nuxt/types/components.d.ts | 327 ++++ .../devtools-app/.nuxt/types/imports.d.ts | 866 +++++++++ .../devtools-app/.nuxt/types/layouts.d.ts | 19 + .../devtools-app/.nuxt/types/middleware.d.ts | 7 + .../devtools-app/.nuxt/types/modules.d.ts | 139 ++ .../.nuxt/types/nitro-config.d.ts | 14 + .../.nuxt/types/nitro-imports.d.ts | 149 ++ .../.nuxt/types/nitro-layouts.d.ts | 17 + .../.nuxt/types/nitro-middleware.d.ts | 11 + .../devtools-app/.nuxt/types/nitro-nuxt.d.ts | 64 + .../.nuxt/types/nitro-routes.d.ts | 17 + packages/devtools-app/.nuxt/types/nitro.d.ts | 3 + .../devtools-app/.nuxt/types/plugins.d.ts | 34 + .../.nuxt/types/runtime-config.d.ts | 36 + .../.nuxt/types/shared-imports.d.ts | 10 + packages/devtools-app/.nuxt/types/ui.d.ts | 36 + .../devtools-app/.nuxt/types/vue-shim.d.ts | 0 .../devtools-app/.nuxt/ui-image-component.ts | 1 + packages/devtools-app/.nuxt/ui.css | 156 ++ packages/devtools-app/.nuxt/ui/accordion.ts | 20 + packages/devtools-app/.nuxt/ui/alert.ts | 264 +++ packages/devtools-app/.nuxt/ui/auth-form.ts | 20 + .../devtools-app/.nuxt/ui/avatar-group.ts | 52 + packages/devtools-app/.nuxt/ui/avatar.ts | 54 + packages/devtools-app/.nuxt/ui/badge.ts | 263 +++ packages/devtools-app/.nuxt/ui/banner.ts | 108 ++ packages/devtools-app/.nuxt/ui/blog-post.ts | 143 ++ packages/devtools-app/.nuxt/ui/blog-posts.ts | 9 + packages/devtools-app/.nuxt/ui/breadcrumb.ts | 45 + .../devtools-app/.nuxt/ui/button-group.ts | 16 + packages/devtools-app/.nuxt/ui/button.ts | 378 ++++ packages/devtools-app/.nuxt/ui/calendar.ts | 331 ++++ packages/devtools-app/.nuxt/ui/card.ts | 34 + packages/devtools-app/.nuxt/ui/carousel.ts | 38 + .../.nuxt/ui/changelog-version.ts | 45 + .../.nuxt/ui/changelog-versions.ts | 8 + .../devtools-app/.nuxt/ui/chat-message.ts | 138 ++ .../devtools-app/.nuxt/ui/chat-messages.ts | 14 + .../devtools-app/.nuxt/ui/chat-palette.ts | 8 + .../.nuxt/ui/chat-prompt-submit.ts | 5 + packages/devtools-app/.nuxt/ui/chat-prompt.ts | 35 + .../devtools-app/.nuxt/ui/chat-reasoning.ts | 36 + .../devtools-app/.nuxt/ui/chat-shimmer.ts | 3 + packages/devtools-app/.nuxt/ui/chat-tool.ts | 61 + .../devtools-app/.nuxt/ui/checkbox-group.ts | 207 +++ packages/devtools-app/.nuxt/ui/checkbox.ts | 237 +++ packages/devtools-app/.nuxt/ui/chip.ts | 96 + packages/devtools-app/.nuxt/ui/collapsible.ts | 6 + .../devtools-app/.nuxt/ui/color-picker.ts | 47 + .../devtools-app/.nuxt/ui/command-palette.ts | 150 ++ packages/devtools-app/.nuxt/ui/container.ts | 3 + .../devtools-app/.nuxt/ui/context-menu.ts | 219 +++ .../devtools-app/.nuxt/ui/dashboard-group.ts | 3 + .../devtools-app/.nuxt/ui/dashboard-navbar.ts | 21 + .../devtools-app/.nuxt/ui/dashboard-panel.ts | 17 + .../.nuxt/ui/dashboard-resize-handle.ts | 3 + .../.nuxt/ui/dashboard-search-button.ts | 15 + .../devtools-app/.nuxt/ui/dashboard-search.ts | 31 + .../.nuxt/ui/dashboard-sidebar-collapse.ts | 9 + .../.nuxt/ui/dashboard-sidebar-toggle.ts | 9 + .../.nuxt/ui/dashboard-sidebar.ts | 37 + .../.nuxt/ui/dashboard-toolbar.ts | 7 + packages/devtools-app/.nuxt/ui/drawer.ts | 149 ++ .../devtools-app/.nuxt/ui/dropdown-menu.ts | 227 +++ .../.nuxt/ui/editor-drag-handle.ts | 6 + .../.nuxt/ui/editor-emoji-menu.ts | 78 + .../.nuxt/ui/editor-mention-menu.ts | 78 + .../.nuxt/ui/editor-suggestion-menu.ts | 78 + .../devtools-app/.nuxt/ui/editor-toolbar.ts | 21 + packages/devtools-app/.nuxt/ui/editor.ts | 55 + packages/devtools-app/.nuxt/ui/empty.ts | 83 + packages/devtools-app/.nuxt/ui/error.ts | 9 + packages/devtools-app/.nuxt/ui/field-group.ts | 16 + packages/devtools-app/.nuxt/ui/file-upload.ts | 290 +++ .../devtools-app/.nuxt/ui/footer-columns.ts | 28 + packages/devtools-app/.nuxt/ui/footer.ts | 11 + packages/devtools-app/.nuxt/ui/form-field.ts | 62 + packages/devtools-app/.nuxt/ui/form.ts | 3 + packages/devtools-app/.nuxt/ui/header.ts | 25 + packages/devtools-app/.nuxt/ui/index.ts | 113 ++ packages/devtools-app/.nuxt/ui/input-date.ts | 360 ++++ packages/devtools-app/.nuxt/ui/input-menu.ts | 484 +++++ .../devtools-app/.nuxt/ui/input-number.ts | 279 +++ packages/devtools-app/.nuxt/ui/input-tags.ts | 333 ++++ packages/devtools-app/.nuxt/ui/input-time.ts | 360 ++++ packages/devtools-app/.nuxt/ui/input.ts | 312 ++++ packages/devtools-app/.nuxt/ui/kbd.ts | 195 ++ packages/devtools-app/.nuxt/ui/link.ts | 22 + packages/devtools-app/.nuxt/ui/main.ts | 3 + packages/devtools-app/.nuxt/ui/marquee.ts | 66 + packages/devtools-app/.nuxt/ui/modal.ts | 60 + .../devtools-app/.nuxt/ui/navigation-menu.ts | 513 ++++++ .../devtools-app/.nuxt/ui/page-anchors.ts | 30 + packages/devtools-app/.nuxt/ui/page-aside.ts | 10 + packages/devtools-app/.nuxt/ui/page-body.ts | 3 + packages/devtools-app/.nuxt/ui/page-card.ts | 274 +++ .../devtools-app/.nuxt/ui/page-columns.ts | 3 + packages/devtools-app/.nuxt/ui/page-cta.ts | 70 + .../devtools-app/.nuxt/ui/page-feature.ts | 34 + packages/devtools-app/.nuxt/ui/page-grid.ts | 3 + packages/devtools-app/.nuxt/ui/page-header.ts | 18 + packages/devtools-app/.nuxt/ui/page-hero.ts | 44 + packages/devtools-app/.nuxt/ui/page-links.ts | 25 + packages/devtools-app/.nuxt/ui/page-list.ts | 8 + packages/devtools-app/.nuxt/ui/page-logos.ts | 15 + .../devtools-app/.nuxt/ui/page-section.ts | 84 + packages/devtools-app/.nuxt/ui/page.ts | 32 + packages/devtools-app/.nuxt/ui/pagination.ts | 13 + packages/devtools-app/.nuxt/ui/pin-input.ts | 194 ++ packages/devtools-app/.nuxt/ui/popover.ts | 6 + .../devtools-app/.nuxt/ui/pricing-plan.ts | 101 ++ .../devtools-app/.nuxt/ui/pricing-plans.ts | 22 + .../devtools-app/.nuxt/ui/pricing-table.ts | 51 + packages/devtools-app/.nuxt/ui/progress.ts | 297 +++ packages/devtools-app/.nuxt/ui/radio-group.ts | 352 ++++ packages/devtools-app/.nuxt/ui/scroll-area.ts | 21 + packages/devtools-app/.nuxt/ui/select-menu.ts | 385 ++++ packages/devtools-app/.nuxt/ui/select.ts | 371 ++++ packages/devtools-app/.nuxt/ui/separator.ts | 172 ++ packages/devtools-app/.nuxt/ui/sidebar.ts | 164 ++ packages/devtools-app/.nuxt/ui/skeleton.ts | 3 + packages/devtools-app/.nuxt/ui/slideover.ts | 132 ++ packages/devtools-app/.nuxt/ui/slider.ts | 171 ++ packages/devtools-app/.nuxt/ui/stepper.ts | 202 +++ packages/devtools-app/.nuxt/ui/switch.ts | 132 ++ packages/devtools-app/.nuxt/ui/table.ts | 162 ++ packages/devtools-app/.nuxt/ui/tabs.ts | 258 +++ packages/devtools-app/.nuxt/ui/textarea.ts | 317 ++++ packages/devtools-app/.nuxt/ui/timeline.ts | 321 ++++ packages/devtools-app/.nuxt/ui/toast.ts | 74 + packages/devtools-app/.nuxt/ui/toaster.ts | 91 + packages/devtools-app/.nuxt/ui/tooltip.ts | 9 + packages/devtools-app/.nuxt/ui/tree.ts | 168 ++ packages/devtools-app/.nuxt/ui/user.ts | 101 ++ packages/devtools-app/.output/nitro.json | 15 + packages/devtools-app/app.vue | 136 ++ packages/devtools-app/assets/css/global.css | 27 + packages/devtools-app/composables/rpc.ts | 33 + packages/devtools-app/composables/state.ts | 85 + packages/devtools-app/composables/tools.ts | 28 + packages/devtools-app/nuxt.config.ts | 51 + packages/devtools-app/package.json | 20 + packages/devtools-app/pages/identity.vue | 475 +++++ packages/devtools-app/pages/index.vue | 208 +++ packages/devtools-app/pages/schema.vue | 244 +++ packages/devtools-app/pages/scripts.vue | 106 ++ packages/devtools-app/pages/serp.vue | 182 ++ packages/devtools-app/tsconfig.json | 3 + .../devtools-app/utils/schema-validation.ts | 470 +++++ packages/devtools/build.config.ts | 18 + packages/devtools/package.json | 70 + packages/devtools/src/bridge.ts | 240 +++ packages/devtools/src/index.ts | 2 + packages/devtools/src/plugin.ts | 26 + .../devtools/src/rpc/functions/get-config.ts | 12 + packages/devtools/src/rpc/index.ts | 1 + packages/devtools/src/rpc/types.ts | 57 + packages/devtools/src/vite.ts | 169 ++ packages/devtools/tsconfig.json | 8 + packages/unhead/src/plugins/validate.ts | 3 + packages/unhead/src/types/head.ts | 5 + packages/unhead/src/types/tags.ts | 5 + packages/vue/src/composables.ts | 7 + packages/vue/src/index.ts | 3 +- packages/vue/src/utils.ts | 13 +- 229 files changed, 21721 insertions(+), 3 deletions(-) create mode 100644 packages/devtools-app/.nuxt/app.config.mjs create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-2IIYX0aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-6KAoyxR_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-995OEiyou9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-PB8l0aO5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-ReiF7Vl5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-XVisnD_cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-_Typ3t9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-q1FXcza0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-qPutQnZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-tzSwS_6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-vrK12kZ24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff create mode 100644 packages/devtools-app/.nuxt/cache/fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/4qBuU9MRVUlPZNPSF7Xom_sK8RBEnfYu-9VXFrdq8A8-8TDwLE1HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/6dYsbWUd_BpKJ7mdDihgOcya1gHXLpJBuMYXux3WMjE-q3fYNS8YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/Lr-hqqZZsYmCt0ITUlr1CUrWim9fsKvoDFZliMxgNHY-iTa_Yt_PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/OknHvWI6KtYn1JQBzX7eSpNDBQ8520F9TvSUJYkVf6A-xeZn9253svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/UA7OtwYHwGN_HjcVGTdmiQxUit7FlqkCwxVUWSeXVnQ-B4OXCFOL_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/du_wVi8LKnmpxxm4zZ_Cr6Hr261AwOLTlejGtcx8ebU-9181D4phd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 create mode 100644 packages/devtools-app/.nuxt/cache/fonts/qxAYvKsXWeYv731eb-h5TRurcdIP_W44mpNdX-HABAk-zUDeMEFlNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 create mode 100644 packages/devtools-app/.nuxt/components.d.ts create mode 100644 packages/devtools-app/.nuxt/imports.d.ts create mode 100644 packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json create mode 100644 packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json create mode 100644 packages/devtools-app/.nuxt/nuxt-fonts-global.css create mode 100644 packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs create mode 100644 packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs create mode 100644 packages/devtools-app/.nuxt/nuxt.d.ts create mode 100644 packages/devtools-app/.nuxt/nuxt.node.d.ts create mode 100644 packages/devtools-app/.nuxt/nuxt.shared.d.ts create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/renderer.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/renderer.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/build/client.precomputed.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/build/client.precomputed.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/nitro/nitro.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/nitro/nitro.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/virtual/_virtual_spa-template.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/chunks/virtual/_virtual_spa-template.mjs.map create mode 100644 packages/devtools-app/.nuxt/prerender/index.mjs create mode 100644 packages/devtools-app/.nuxt/prerender/index.mjs.map create mode 100644 packages/devtools-app/.nuxt/schema/nuxt.schema.d.ts create mode 100644 packages/devtools-app/.nuxt/schema/nuxt.schema.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.app.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.node.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.server.json create mode 100644 packages/devtools-app/.nuxt/tsconfig.shared.json create mode 100644 packages/devtools-app/.nuxt/types/app.config.d.ts create mode 100644 packages/devtools-app/.nuxt/types/build.d.ts create mode 100644 packages/devtools-app/.nuxt/types/builder-env.d.ts create mode 100644 packages/devtools-app/.nuxt/types/components.d.ts create mode 100644 packages/devtools-app/.nuxt/types/imports.d.ts create mode 100644 packages/devtools-app/.nuxt/types/layouts.d.ts create mode 100644 packages/devtools-app/.nuxt/types/middleware.d.ts create mode 100644 packages/devtools-app/.nuxt/types/modules.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-config.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-imports.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-layouts.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-middleware.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-nuxt.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro-routes.d.ts create mode 100644 packages/devtools-app/.nuxt/types/nitro.d.ts create mode 100644 packages/devtools-app/.nuxt/types/plugins.d.ts create mode 100644 packages/devtools-app/.nuxt/types/runtime-config.d.ts create mode 100644 packages/devtools-app/.nuxt/types/shared-imports.d.ts create mode 100644 packages/devtools-app/.nuxt/types/ui.d.ts create mode 100644 packages/devtools-app/.nuxt/types/vue-shim.d.ts create mode 100644 packages/devtools-app/.nuxt/ui-image-component.ts create mode 100644 packages/devtools-app/.nuxt/ui.css create mode 100644 packages/devtools-app/.nuxt/ui/accordion.ts create mode 100644 packages/devtools-app/.nuxt/ui/alert.ts create mode 100644 packages/devtools-app/.nuxt/ui/auth-form.ts create mode 100644 packages/devtools-app/.nuxt/ui/avatar-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/avatar.ts create mode 100644 packages/devtools-app/.nuxt/ui/badge.ts create mode 100644 packages/devtools-app/.nuxt/ui/banner.ts create mode 100644 packages/devtools-app/.nuxt/ui/blog-post.ts create mode 100644 packages/devtools-app/.nuxt/ui/blog-posts.ts create mode 100644 packages/devtools-app/.nuxt/ui/breadcrumb.ts create mode 100644 packages/devtools-app/.nuxt/ui/button-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/button.ts create mode 100644 packages/devtools-app/.nuxt/ui/calendar.ts create mode 100644 packages/devtools-app/.nuxt/ui/card.ts create mode 100644 packages/devtools-app/.nuxt/ui/carousel.ts create mode 100644 packages/devtools-app/.nuxt/ui/changelog-version.ts create mode 100644 packages/devtools-app/.nuxt/ui/changelog-versions.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-message.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-messages.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-palette.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-prompt-submit.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-prompt.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-reasoning.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-shimmer.ts create mode 100644 packages/devtools-app/.nuxt/ui/chat-tool.ts create mode 100644 packages/devtools-app/.nuxt/ui/checkbox-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/checkbox.ts create mode 100644 packages/devtools-app/.nuxt/ui/chip.ts create mode 100644 packages/devtools-app/.nuxt/ui/collapsible.ts create mode 100644 packages/devtools-app/.nuxt/ui/color-picker.ts create mode 100644 packages/devtools-app/.nuxt/ui/command-palette.ts create mode 100644 packages/devtools-app/.nuxt/ui/container.ts create mode 100644 packages/devtools-app/.nuxt/ui/context-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-navbar.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-panel.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-resize-handle.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-search-button.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-search.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-sidebar-collapse.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-sidebar-toggle.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-sidebar.ts create mode 100644 packages/devtools-app/.nuxt/ui/dashboard-toolbar.ts create mode 100644 packages/devtools-app/.nuxt/ui/drawer.ts create mode 100644 packages/devtools-app/.nuxt/ui/dropdown-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-drag-handle.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-emoji-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-mention-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-suggestion-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor-toolbar.ts create mode 100644 packages/devtools-app/.nuxt/ui/editor.ts create mode 100644 packages/devtools-app/.nuxt/ui/empty.ts create mode 100644 packages/devtools-app/.nuxt/ui/error.ts create mode 100644 packages/devtools-app/.nuxt/ui/field-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/file-upload.ts create mode 100644 packages/devtools-app/.nuxt/ui/footer-columns.ts create mode 100644 packages/devtools-app/.nuxt/ui/footer.ts create mode 100644 packages/devtools-app/.nuxt/ui/form-field.ts create mode 100644 packages/devtools-app/.nuxt/ui/form.ts create mode 100644 packages/devtools-app/.nuxt/ui/header.ts create mode 100644 packages/devtools-app/.nuxt/ui/index.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-date.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-number.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-tags.ts create mode 100644 packages/devtools-app/.nuxt/ui/input-time.ts create mode 100644 packages/devtools-app/.nuxt/ui/input.ts create mode 100644 packages/devtools-app/.nuxt/ui/kbd.ts create mode 100644 packages/devtools-app/.nuxt/ui/link.ts create mode 100644 packages/devtools-app/.nuxt/ui/main.ts create mode 100644 packages/devtools-app/.nuxt/ui/marquee.ts create mode 100644 packages/devtools-app/.nuxt/ui/modal.ts create mode 100644 packages/devtools-app/.nuxt/ui/navigation-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-anchors.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-aside.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-body.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-card.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-columns.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-cta.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-feature.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-grid.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-header.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-hero.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-links.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-list.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-logos.ts create mode 100644 packages/devtools-app/.nuxt/ui/page-section.ts create mode 100644 packages/devtools-app/.nuxt/ui/page.ts create mode 100644 packages/devtools-app/.nuxt/ui/pagination.ts create mode 100644 packages/devtools-app/.nuxt/ui/pin-input.ts create mode 100644 packages/devtools-app/.nuxt/ui/popover.ts create mode 100644 packages/devtools-app/.nuxt/ui/pricing-plan.ts create mode 100644 packages/devtools-app/.nuxt/ui/pricing-plans.ts create mode 100644 packages/devtools-app/.nuxt/ui/pricing-table.ts create mode 100644 packages/devtools-app/.nuxt/ui/progress.ts create mode 100644 packages/devtools-app/.nuxt/ui/radio-group.ts create mode 100644 packages/devtools-app/.nuxt/ui/scroll-area.ts create mode 100644 packages/devtools-app/.nuxt/ui/select-menu.ts create mode 100644 packages/devtools-app/.nuxt/ui/select.ts create mode 100644 packages/devtools-app/.nuxt/ui/separator.ts create mode 100644 packages/devtools-app/.nuxt/ui/sidebar.ts create mode 100644 packages/devtools-app/.nuxt/ui/skeleton.ts create mode 100644 packages/devtools-app/.nuxt/ui/slideover.ts create mode 100644 packages/devtools-app/.nuxt/ui/slider.ts create mode 100644 packages/devtools-app/.nuxt/ui/stepper.ts create mode 100644 packages/devtools-app/.nuxt/ui/switch.ts create mode 100644 packages/devtools-app/.nuxt/ui/table.ts create mode 100644 packages/devtools-app/.nuxt/ui/tabs.ts create mode 100644 packages/devtools-app/.nuxt/ui/textarea.ts create mode 100644 packages/devtools-app/.nuxt/ui/timeline.ts create mode 100644 packages/devtools-app/.nuxt/ui/toast.ts create mode 100644 packages/devtools-app/.nuxt/ui/toaster.ts create mode 100644 packages/devtools-app/.nuxt/ui/tooltip.ts create mode 100644 packages/devtools-app/.nuxt/ui/tree.ts create mode 100644 packages/devtools-app/.nuxt/ui/user.ts create mode 100644 packages/devtools-app/.output/nitro.json create mode 100644 packages/devtools-app/app.vue create mode 100644 packages/devtools-app/assets/css/global.css create mode 100644 packages/devtools-app/composables/rpc.ts create mode 100644 packages/devtools-app/composables/state.ts create mode 100644 packages/devtools-app/composables/tools.ts create mode 100644 packages/devtools-app/nuxt.config.ts create mode 100644 packages/devtools-app/package.json create mode 100644 packages/devtools-app/pages/identity.vue create mode 100644 packages/devtools-app/pages/index.vue create mode 100644 packages/devtools-app/pages/schema.vue create mode 100644 packages/devtools-app/pages/scripts.vue create mode 100644 packages/devtools-app/pages/serp.vue create mode 100644 packages/devtools-app/tsconfig.json create mode 100644 packages/devtools-app/utils/schema-validation.ts create mode 100644 packages/devtools/build.config.ts create mode 100644 packages/devtools/package.json create mode 100644 packages/devtools/src/bridge.ts create mode 100644 packages/devtools/src/index.ts create mode 100644 packages/devtools/src/plugin.ts create mode 100644 packages/devtools/src/rpc/functions/get-config.ts create mode 100644 packages/devtools/src/rpc/index.ts create mode 100644 packages/devtools/src/rpc/types.ts create mode 100644 packages/devtools/src/vite.ts create mode 100644 packages/devtools/tsconfig.json diff --git a/examples/vite-ssr-vue/vite.config.ts b/examples/vite-ssr-vue/vite.config.ts index 91591fc3f..be990b0b5 100644 --- a/examples/vite-ssr-vue/vite.config.ts +++ b/examples/vite-ssr-vue/vite.config.ts @@ -1,17 +1,21 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import { DevTools } from '@vitejs/devtools' import { unheadVueComposablesImports } from '@unhead/vue' import AutoImport from 'unplugin-auto-import/vite' +import { unheadDevtools } from '@unhead/devtools/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [ + DevTools({ clientAuth: false }), AutoImport({ imports: [ unheadVueComposablesImports, 'vue', ], }), - vue() + vue(), + unheadDevtools(), ], }) diff --git a/packages/devtools-app/.nuxt/app.config.mjs b/packages/devtools-app/.nuxt/app.config.mjs new file mode 100644 index 000000000..aab26c4a7 --- /dev/null +++ b/packages/devtools-app/.nuxt/app.config.mjs @@ -0,0 +1,317 @@ + +import { defuFn } from 'defu' + +const inlineConfig = { + "nuxt": {}, + "ui": { + "colors": { + "primary": "green", + "secondary": "blue", + "success": "green", + "info": "blue", + "warning": "yellow", + "error": "red", + "neutral": "slate" + }, + "icons": { + "arrowDown": "i-lucide-arrow-down", + "arrowLeft": "i-lucide-arrow-left", + "arrowRight": "i-lucide-arrow-right", + "arrowUp": "i-lucide-arrow-up", + "caution": "i-lucide-circle-alert", + "check": "i-lucide-check", + "chevronDoubleLeft": "i-lucide-chevrons-left", + "chevronDoubleRight": "i-lucide-chevrons-right", + "chevronDown": "i-lucide-chevron-down", + "chevronLeft": "i-lucide-chevron-left", + "chevronRight": "i-lucide-chevron-right", + "chevronUp": "i-lucide-chevron-up", + "close": "i-lucide-x", + "copy": "i-lucide-copy", + "copyCheck": "i-lucide-copy-check", + "dark": "i-lucide-moon", + "drag": "i-lucide-grip-vertical", + "ellipsis": "i-lucide-ellipsis", + "error": "i-lucide-circle-x", + "external": "i-lucide-arrow-up-right", + "eye": "i-lucide-eye", + "eyeOff": "i-lucide-eye-off", + "file": "i-lucide-file", + "folder": "i-lucide-folder", + "folderOpen": "i-lucide-folder-open", + "hash": "i-lucide-hash", + "info": "i-lucide-info", + "light": "i-lucide-sun", + "loading": "i-lucide-loader-circle", + "menu": "i-lucide-menu", + "minus": "i-lucide-minus", + "panelClose": "i-lucide-panel-left-close", + "panelOpen": "i-lucide-panel-left-open", + "plus": "i-lucide-plus", + "reload": "i-lucide-rotate-ccw", + "search": "i-lucide-search", + "stop": "i-lucide-square", + "success": "i-lucide-circle-check", + "system": "i-lucide-monitor", + "tip": "i-lucide-lightbulb", + "upload": "i-lucide-upload", + "warning": "i-lucide-triangle-alert" + }, + "tv": { + "twMergeConfig": {} + } + }, + "icon": { + "provider": "iconify", + "class": "", + "aliases": {}, + "iconifyApiEndpoint": "https://api.iconify.design", + "localApiEndpoint": "/api/_nuxt_icon", + "fallbackToApi": true, + "cssSelectorPrefix": "i-", + "cssWherePseudo": true, + "cssLayer": "base", + "mode": "css", + "attrs": { + "aria-hidden": true + }, + "collections": [ + "academicons", + "akar-icons", + "ant-design", + "arcticons", + "basil", + "bi", + "bitcoin-icons", + "bpmn", + "brandico", + "bx", + "bxl", + "bxs", + "bytesize", + "carbon", + "catppuccin", + "cbi", + "charm", + "ci", + "cib", + "cif", + "cil", + "circle-flags", + "circum", + "clarity", + "codex", + "codicon", + "covid", + "cryptocurrency", + "cryptocurrency-color", + "cuida", + "dashicons", + "devicon", + "devicon-plain", + "dinkie-icons", + "duo-icons", + "ei", + "el", + "emojione", + "emojione-monotone", + "emojione-v1", + "entypo", + "entypo-social", + "eos-icons", + "ep", + "et", + "eva", + "f7", + "fa", + "fa-brands", + "fa-regular", + "fa-solid", + "fa6-brands", + "fa6-regular", + "fa6-solid", + "fa7-brands", + "fa7-regular", + "fa7-solid", + "fad", + "famicons", + "fe", + "feather", + "file-icons", + "flag", + "flagpack", + "flat-color-icons", + "flat-ui", + "flowbite", + "fluent", + "fluent-color", + "fluent-emoji", + "fluent-emoji-flat", + "fluent-emoji-high-contrast", + "fluent-mdl2", + "fontelico", + "fontisto", + "formkit", + "foundation", + "fxemoji", + "gala", + "game-icons", + "garden", + "geo", + "gg", + "gis", + "gravity-ui", + "gridicons", + "grommet-icons", + "guidance", + "healthicons", + "heroicons", + "heroicons-outline", + "heroicons-solid", + "hugeicons", + "humbleicons", + "ic", + "icomoon-free", + "icon-park", + "icon-park-outline", + "icon-park-solid", + "icon-park-twotone", + "iconamoon", + "iconoir", + "icons8", + "il", + "ion", + "iwwa", + "ix", + "jam", + "la", + "lets-icons", + "line-md", + "lineicons", + "logos", + "ls", + "lsicon", + "lucide", + "lucide-lab", + "mage", + "majesticons", + "maki", + "map", + "marketeq", + "material-icon-theme", + "material-symbols", + "material-symbols-light", + "mdi", + "mdi-light", + "medical-icon", + "memory", + "meteocons", + "meteor-icons", + "mi", + "mingcute", + "mono-icons", + "mynaui", + "nimbus", + "nonicons", + "noto", + "noto-v1", + "nrk", + "octicon", + "oi", + "ooui", + "openmoji", + "oui", + "pajamas", + "pepicons", + "pepicons-pencil", + "pepicons-pop", + "pepicons-print", + "ph", + "picon", + "pixel", + "pixelarticons", + "prime", + "proicons", + "ps", + "qlementine-icons", + "quill", + "radix-icons", + "raphael", + "ri", + "rivet-icons", + "roentgen", + "si", + "si-glyph", + "sidekickicons", + "simple-icons", + "simple-line-icons", + "skill-icons", + "solar", + "stash", + "streamline", + "streamline-block", + "streamline-color", + "streamline-cyber", + "streamline-cyber-color", + "streamline-emojis", + "streamline-flex", + "streamline-flex-color", + "streamline-freehand", + "streamline-freehand-color", + "streamline-kameleon-color", + "streamline-logos", + "streamline-pixel", + "streamline-plump", + "streamline-plump-color", + "streamline-sharp", + "streamline-sharp-color", + "streamline-stickies-color", + "streamline-ultimate", + "streamline-ultimate-color", + "subway", + "svg-spinners", + "system-uicons", + "tabler", + "tdesign", + "teenyicons", + "temaki", + "token", + "token-branded", + "topcoat", + "twemoji", + "typcn", + "uil", + "uim", + "uis", + "uit", + "uiw", + "unjs", + "vaadin", + "vs", + "vscode-icons", + "websymbol", + "weui", + "whh", + "wi", + "wpf", + "zmdi", + "zondicons" + ], + "fetchTimeout": 1500 + } +} + +/** client **/ +import { _replaceAppConfig } from '#app/config' + +// Vite - webpack is handled directly in #app/config +if (import.meta.dev && !import.meta.nitro && import.meta.hot) { + import.meta.hot.accept((newModule) => { + _replaceAppConfig(newModule.default) + }) +} +/** client-end **/ + + + +export default /*@__PURE__*/ defuFn(inlineConfig) diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-2IIYX0aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-2IIYX0aNjjEURwJNawIOc3sV0QzpbwUrCv8wl3ZGgFA.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-6KAoyxR_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-6KAoyxR_H06JoWqdqVS0-kmvTJ84w1VV-vpL07MkGFo.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-995OEiyou9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-995OEiyou9G2dJsMNn9aLuHTFlgwosm_9gD5SQUWR40.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-D6hedAgqRfOCLZzaShnyeAvlEnMzk4Wm7g9WDKWFHIc.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-PB8l0aO5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-PB8l0aO5_Gv8N1f__GTo60CpiyCHnESCVbnMuj6po8g.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-ReiF7Vl5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-ReiF7Vl5j3fA_MaPuQPt4ANBQ5qTkjFYFTlL1W3ym1w.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-XVisnD_cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-XVisnD_cOlmAZf__QN8j1eV5xiztJ4ZpF33oUzZ3Wb4.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-_Typ3t9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-_Typ3t9johw0N4fC56HXu9YDMYLoTZ2lSFRo85hF34Q.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-q1FXcza0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-q1FXcza0fo5JeXlwnX_f_cA4m33FrdrDAf5fxWhk_n4.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-qPutQnZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-qPutQnZOK_xLpYlL8vTDDTLjS3Dq9I6CqdH-cvt9zFA.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-tzSwS_6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-tzSwS_6EmLa_L3rQTreHB3obQJeSPNBPty8uslTYEFY.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-vrK12kZ24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff b/packages/devtools-app/.nuxt/cache/fonts/1ZTlEDqU4DtwDJiND8f6qaugUpa0RIDvQl-v7iM6l54-vrK12kZ24WGUghky9YxsOZpLH8Tt4jMU8XIvzgLQ6DU.woff new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 b/packages/devtools-app/.nuxt/cache/fonts/4ppnHhMi-pBsWSPo7mY0avYxlDoAg1N3PTzCwXLZ5rA-d9oibkGnTd1JL3tc_xnaVgBLYmOB8kjrK2cvZaqwj9s.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/4qBuU9MRVUlPZNPSF7Xom_sK8RBEnfYu-9VXFrdq8A8-8TDwLE1HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 b/packages/devtools-app/.nuxt/cache/fonts/4qBuU9MRVUlPZNPSF7Xom_sK8RBEnfYu-9VXFrdq8A8-8TDwLE1HAj1sQn7XxVWtM_7sIaPM-DTdO3Pf8U2DF1U.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/6dYsbWUd_BpKJ7mdDihgOcya1gHXLpJBuMYXux3WMjE-q3fYNS8YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 b/packages/devtools-app/.nuxt/cache/fonts/6dYsbWUd_BpKJ7mdDihgOcya1gHXLpJBuMYXux3WMjE-q3fYNS8YbW5n7ZeXI2vSNgkRWW5VDPKAl51SNTjG2qk.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/Lr-hqqZZsYmCt0ITUlr1CUrWim9fsKvoDFZliMxgNHY-iTa_Yt_PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 b/packages/devtools-app/.nuxt/cache/fonts/Lr-hqqZZsYmCt0ITUlr1CUrWim9fsKvoDFZliMxgNHY-iTa_Yt_PzhOY9TX7ZXdSlEPim6iRt92xhECwaxWxd5w.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/OknHvWI6KtYn1JQBzX7eSpNDBQ8520F9TvSUJYkVf6A-xeZn9253svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 b/packages/devtools-app/.nuxt/cache/fonts/OknHvWI6KtYn1JQBzX7eSpNDBQ8520F9TvSUJYkVf6A-xeZn9253svK_8Q2LD0XEruY_MnEsuCRO5LenPoggC0Y.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 b/packages/devtools-app/.nuxt/cache/fonts/PV2hrQG6wq5BlIPDjdL1IcOflycaghyt5MHzlBqZtlo-lb_WexLz3VZqfTN0oi554iBH5tT2j2UFEV-XErCAS3E.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/UA7OtwYHwGN_HjcVGTdmiQxUit7FlqkCwxVUWSeXVnQ-B4OXCFOL_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 b/packages/devtools-app/.nuxt/cache/fonts/UA7OtwYHwGN_HjcVGTdmiQxUit7FlqkCwxVUWSeXVnQ-B4OXCFOL_tWrYODpQTc07aMaj0c2cewTOmBRWR9tD-A.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 b/packages/devtools-app/.nuxt/cache/fonts/VE4cDVCv5MxbFM7ZLoLCGbIpNd71zhp7MDI9lmN5Y7I-xZyDYCUVrd6LV8eVGF3Um3UZjBFuUtDGtvdyTBBRYBo.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/du_wVi8LKnmpxxm4zZ_Cr6Hr261AwOLTlejGtcx8ebU-9181D4phd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 b/packages/devtools-app/.nuxt/cache/fonts/du_wVi8LKnmpxxm4zZ_Cr6Hr261AwOLTlejGtcx8ebU-9181D4phd7f4CBS6wuIfZf5gs5WaADVsAaDTM9PYZig.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 b/packages/devtools-app/.nuxt/cache/fonts/fVoGbnMbBFd5L9BBp9fUPavUSkZ_EmsQNSyadkT-108-U4T0khaeLQSIhtt9eVvaCEKJjtWJ4ioRJOf8hvqkWY0.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 b/packages/devtools-app/.nuxt/cache/fonts/lQAxeCEs1R0Lw-H9XRU1RlOARQN8J6npRsPjyEDMe5s-_DUSLEkO3tKTuun_gSnDLoQPVEnpOnyqZMOw0ByZ6PA.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 b/packages/devtools-app/.nuxt/cache/fonts/lntlqNHKLV2n82yTwMde70QqOjcfLE2XJ5oKZ3vRPWc-z6TxpIZQdWXztWLr9_OFWqt_WJJoeGtuK_-XQMZGQwE.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/cache/fonts/qxAYvKsXWeYv731eb-h5TRurcdIP_W44mpNdX-HABAk-zUDeMEFlNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 b/packages/devtools-app/.nuxt/cache/fonts/qxAYvKsXWeYv731eb-h5TRurcdIP_W44mpNdX-HABAk-zUDeMEFlNtNbrwvT9JxLEBg0TphGy70O6RfIoIX_ZwU.woff2 new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/components.d.ts b/packages/devtools-app/.nuxt/components.d.ts new file mode 100644 index 000000000..12e2dd1f6 --- /dev/null +++ b/packages/devtools-app/.nuxt/components.d.ts @@ -0,0 +1,322 @@ + +import type { DefineComponent, SlotsType } from 'vue' +type IslandComponent = DefineComponent<{}, {refresh: () => Promise}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T + +type HydrationStrategies = { + hydrateOnVisible?: IntersectionObserverInit | true + hydrateOnIdle?: number | true + hydrateOnInteraction?: keyof HTMLElementEventMap | Array | true + hydrateOnMediaQuery?: string + hydrateAfter?: number + hydrateWhen?: boolean + hydrateNever?: true +} +type LazyComponent = DefineComponent void }> & T + + +export const UColorModeAvatar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeAvatar.vue")['default'] +export const UColorModeButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeButton.vue")['default'] +export const UColorModeImage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeImage.vue")['default'] +export const UColorModeSelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSelect.vue")['default'] +export const UColorModeSwitch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSwitch.vue")['default'] +export const UAccordion: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Accordion.vue")['default'] +export const UAlert: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue")['default'] +export const UApp: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/App.vue")['default'] +export const UAuthForm: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AuthForm.vue")['default'] +export const UAvatar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Avatar.vue")['default'] +export const UAvatarGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AvatarGroup.vue")['default'] +export const UBadge: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue")['default'] +export const UBanner: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Banner.vue")['default'] +export const UBlogPost: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPost.vue")['default'] +export const UBlogPosts: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPosts.vue")['default'] +export const UBreadcrumb: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Breadcrumb.vue")['default'] +export const UButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Button.vue")['default'] +export const UCalendar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Calendar.vue")['default'] +export const UCard: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Card.vue")['default'] +export const UCarousel: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Carousel.vue")['default'] +export const UChangelogVersion: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersion.vue")['default'] +export const UChangelogVersions: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersions.vue")['default'] +export const UChatMessage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessage.vue")['default'] +export const UChatMessages: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessages.vue")['default'] +export const UChatPalette: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPalette.vue")['default'] +export const UChatPrompt: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPrompt.vue")['default'] +export const UChatPromptSubmit: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPromptSubmit.vue")['default'] +export const UChatReasoning: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatReasoning.vue")['default'] +export const UChatShimmer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatShimmer.vue")['default'] +export const UChatTool: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatTool.vue")['default'] +export const UCheckbox: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue")['default'] +export const UCheckboxGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CheckboxGroup.vue")['default'] +export const UChip: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Chip.vue")['default'] +export const UCollapsible: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Collapsible.vue")['default'] +export const UColorPicker: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ColorPicker.vue")['default'] +export const UCommandPalette: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CommandPalette.vue")['default'] +export const UContainer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Container.vue")['default'] +export const UContextMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue")['default'] +export const UContextMenuContent: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenuContent.vue")['default'] +export const UDashboardGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardGroup.vue")['default'] +export const UDashboardNavbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardNavbar.vue")['default'] +export const UDashboardPanel: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardPanel.vue")['default'] +export const UDashboardResizeHandle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardResizeHandle.vue")['default'] +export const UDashboardSearch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearch.vue")['default'] +export const UDashboardSearchButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearchButton.vue")['default'] +export const UDashboardSidebar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebar.vue")['default'] +export const UDashboardSidebarCollapse: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarCollapse.vue")['default'] +export const UDashboardSidebarToggle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarToggle.vue")['default'] +export const UDashboardToolbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardToolbar.vue")['default'] +export const UDrawer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue")['default'] +export const UDropdownMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue")['default'] +export const UDropdownMenuContent: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenuContent.vue")['default'] +export const UEditor: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Editor.vue")['default'] +export const UEditorDragHandle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorDragHandle.vue")['default'] +export const UEditorEmojiMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorEmojiMenu.vue")['default'] +export const UEditorMentionMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorMentionMenu.vue")['default'] +export const UEditorSuggestionMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorSuggestionMenu.vue")['default'] +export const UEditorToolbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorToolbar.vue")['default'] +export const UEmpty: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Empty.vue")['default'] +export const UError: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Error.vue")['default'] +export const UFieldGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FieldGroup.vue")['default'] +export const UFileUpload: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FileUpload.vue")['default'] +export const UFooter: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Footer.vue")['default'] +export const UFooterColumns: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FooterColumns.vue")['default'] +export const UForm: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Form.vue")['default'] +export const UFormField: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue")['default'] +export const UHeader: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Header.vue")['default'] +export const UIcon: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Icon.vue")['default'] +export const UInput: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Input.vue")['default'] +export const UInputDate: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputDate.vue")['default'] +export const UInputMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputMenu.vue")['default'] +export const UInputNumber: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue")['default'] +export const UInputTags: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTags.vue")['default'] +export const UInputTime: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTime.vue")['default'] +export const UKbd: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Kbd.vue")['default'] +export const ULink: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Link.vue")['default'] +export const ULinkBase: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/LinkBase.vue")['default'] +export const UMain: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Main.vue")['default'] +export const UMarquee: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Marquee.vue")['default'] +export const UModal: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue")['default'] +export const UNavigationMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue")['default'] +export const UOverlayProvider: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/OverlayProvider.vue")['default'] +export const UPage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Page.vue")['default'] +export const UPageAnchors: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAnchors.vue")['default'] +export const UPageAside: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAside.vue")['default'] +export const UPageBody: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageBody.vue")['default'] +export const UPageCTA: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCTA.vue")['default'] +export const UPageCard: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCard.vue")['default'] +export const UPageColumns: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageColumns.vue")['default'] +export const UPageFeature: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageFeature.vue")['default'] +export const UPageGrid: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageGrid.vue")['default'] +export const UPageHeader: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHeader.vue")['default'] +export const UPageHero: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHero.vue")['default'] +export const UPageLinks: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLinks.vue")['default'] +export const UPageList: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageList.vue")['default'] +export const UPageLogos: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLogos.vue")['default'] +export const UPageSection: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageSection.vue")['default'] +export const UPagination: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue")['default'] +export const UPinInput: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PinInput.vue")['default'] +export const UPopover: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue")['default'] +export const UPricingPlan: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlan.vue")['default'] +export const UPricingPlans: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlans.vue")['default'] +export const UPricingTable: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingTable.vue")['default'] +export const UProgress: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue")['default'] +export const URadioGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/RadioGroup.vue")['default'] +export const UScrollArea: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ScrollArea.vue")['default'] +export const USelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Select.vue")['default'] +export const USelectMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue")['default'] +export const USeparator: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Separator.vue")['default'] +export const USidebar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Sidebar.vue")['default'] +export const USkeleton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Skeleton.vue")['default'] +export const USlideover: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slideover.vue")['default'] +export const USlider: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slider.vue")['default'] +export const UStepper: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Stepper.vue")['default'] +export const USwitch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue")['default'] +export const UTable: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Table.vue")['default'] +export const UTabs: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue")['default'] +export const UTextarea: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue")['default'] +export const UTheme: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Theme.vue")['default'] +export const UTimeline: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Timeline.vue")['default'] +export const UToast: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toast.vue")['default'] +export const UToaster: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toaster.vue")['default'] +export const UTooltip: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue")['default'] +export const UTree: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tree.vue")['default'] +export const UUser: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/User.vue")['default'] +export const ULocaleSelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/locale/LocaleSelect.vue")['default'] +export const NuxtWelcome: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/welcome.vue")['default'] +export const NuxtLayout: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-layout")['default'] +export const NuxtErrorBoundary: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default'] +export const ClientOnly: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/client-only")['default'] +export const DevOnly: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/dev-only")['default'] +export const ServerPlaceholder: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/server-placeholder")['default'] +export const NuxtLink: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-link")['default'] +export const NuxtLoadingIndicator: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default'] +export const NuxtTime: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-time.vue")['default'] +export const NuxtRouteAnnouncer: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default'] +export const NuxtAnnouncer: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-announcer")['default'] +export const NuxtImg: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtImg'] +export const NuxtPicture: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtPicture'] +export const NuxtPage: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/pages/runtime/page")['default'] +export const NoScript: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['NoScript'] +export const Link: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Link'] +export const Base: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Base'] +export const Title: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Title'] +export const Meta: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Meta'] +export const Style: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Style'] +export const Head: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Head'] +export const Html: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Html'] +export const Body: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Body'] +export const Icon: typeof import("../../../node_modules/.pnpm/@nuxt+icon@2.2.1_magicast@0.5.2_vite@8.0.6_vue@3.5.32_typescript@6.0.2_/node_modules/@nuxt/icon/dist/runtime/components/index")['default'] +export const ColorScheme: typeof import("../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/component.vue3.vue")['default'] +export const NuxtIsland: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-island")['default'] +export const LazyUColorModeAvatar: LazyComponent +export const LazyUColorModeButton: LazyComponent +export const LazyUColorModeImage: LazyComponent +export const LazyUColorModeSelect: LazyComponent +export const LazyUColorModeSwitch: LazyComponent +export const LazyUAccordion: LazyComponent +export const LazyUAlert: LazyComponent +export const LazyUApp: LazyComponent +export const LazyUAuthForm: LazyComponent +export const LazyUAvatar: LazyComponent +export const LazyUAvatarGroup: LazyComponent +export const LazyUBadge: LazyComponent +export const LazyUBanner: LazyComponent +export const LazyUBlogPost: LazyComponent +export const LazyUBlogPosts: LazyComponent +export const LazyUBreadcrumb: LazyComponent +export const LazyUButton: LazyComponent +export const LazyUCalendar: LazyComponent +export const LazyUCard: LazyComponent +export const LazyUCarousel: LazyComponent +export const LazyUChangelogVersion: LazyComponent +export const LazyUChangelogVersions: LazyComponent +export const LazyUChatMessage: LazyComponent +export const LazyUChatMessages: LazyComponent +export const LazyUChatPalette: LazyComponent +export const LazyUChatPrompt: LazyComponent +export const LazyUChatPromptSubmit: LazyComponent +export const LazyUChatReasoning: LazyComponent +export const LazyUChatShimmer: LazyComponent +export const LazyUChatTool: LazyComponent +export const LazyUCheckbox: LazyComponent +export const LazyUCheckboxGroup: LazyComponent +export const LazyUChip: LazyComponent +export const LazyUCollapsible: LazyComponent +export const LazyUColorPicker: LazyComponent +export const LazyUCommandPalette: LazyComponent +export const LazyUContainer: LazyComponent +export const LazyUContextMenu: LazyComponent +export const LazyUContextMenuContent: LazyComponent +export const LazyUDashboardGroup: LazyComponent +export const LazyUDashboardNavbar: LazyComponent +export const LazyUDashboardPanel: LazyComponent +export const LazyUDashboardResizeHandle: LazyComponent +export const LazyUDashboardSearch: LazyComponent +export const LazyUDashboardSearchButton: LazyComponent +export const LazyUDashboardSidebar: LazyComponent +export const LazyUDashboardSidebarCollapse: LazyComponent +export const LazyUDashboardSidebarToggle: LazyComponent +export const LazyUDashboardToolbar: LazyComponent +export const LazyUDrawer: LazyComponent +export const LazyUDropdownMenu: LazyComponent +export const LazyUDropdownMenuContent: LazyComponent +export const LazyUEditor: LazyComponent +export const LazyUEditorDragHandle: LazyComponent +export const LazyUEditorEmojiMenu: LazyComponent +export const LazyUEditorMentionMenu: LazyComponent +export const LazyUEditorSuggestionMenu: LazyComponent +export const LazyUEditorToolbar: LazyComponent +export const LazyUEmpty: LazyComponent +export const LazyUError: LazyComponent +export const LazyUFieldGroup: LazyComponent +export const LazyUFileUpload: LazyComponent +export const LazyUFooter: LazyComponent +export const LazyUFooterColumns: LazyComponent +export const LazyUForm: LazyComponent +export const LazyUFormField: LazyComponent +export const LazyUHeader: LazyComponent +export const LazyUIcon: LazyComponent +export const LazyUInput: LazyComponent +export const LazyUInputDate: LazyComponent +export const LazyUInputMenu: LazyComponent +export const LazyUInputNumber: LazyComponent +export const LazyUInputTags: LazyComponent +export const LazyUInputTime: LazyComponent +export const LazyUKbd: LazyComponent +export const LazyULink: LazyComponent +export const LazyULinkBase: LazyComponent +export const LazyUMain: LazyComponent +export const LazyUMarquee: LazyComponent +export const LazyUModal: LazyComponent +export const LazyUNavigationMenu: LazyComponent +export const LazyUOverlayProvider: LazyComponent +export const LazyUPage: LazyComponent +export const LazyUPageAnchors: LazyComponent +export const LazyUPageAside: LazyComponent +export const LazyUPageBody: LazyComponent +export const LazyUPageCTA: LazyComponent +export const LazyUPageCard: LazyComponent +export const LazyUPageColumns: LazyComponent +export const LazyUPageFeature: LazyComponent +export const LazyUPageGrid: LazyComponent +export const LazyUPageHeader: LazyComponent +export const LazyUPageHero: LazyComponent +export const LazyUPageLinks: LazyComponent +export const LazyUPageList: LazyComponent +export const LazyUPageLogos: LazyComponent +export const LazyUPageSection: LazyComponent +export const LazyUPagination: LazyComponent +export const LazyUPinInput: LazyComponent +export const LazyUPopover: LazyComponent +export const LazyUPricingPlan: LazyComponent +export const LazyUPricingPlans: LazyComponent +export const LazyUPricingTable: LazyComponent +export const LazyUProgress: LazyComponent +export const LazyURadioGroup: LazyComponent +export const LazyUScrollArea: LazyComponent +export const LazyUSelect: LazyComponent +export const LazyUSelectMenu: LazyComponent +export const LazyUSeparator: LazyComponent +export const LazyUSidebar: LazyComponent +export const LazyUSkeleton: LazyComponent +export const LazyUSlideover: LazyComponent +export const LazyUSlider: LazyComponent +export const LazyUStepper: LazyComponent +export const LazyUSwitch: LazyComponent +export const LazyUTable: LazyComponent +export const LazyUTabs: LazyComponent +export const LazyUTextarea: LazyComponent +export const LazyUTheme: LazyComponent +export const LazyUTimeline: LazyComponent +export const LazyUToast: LazyComponent +export const LazyUToaster: LazyComponent +export const LazyUTooltip: LazyComponent +export const LazyUTree: LazyComponent +export const LazyUUser: LazyComponent +export const LazyULocaleSelect: LazyComponent +export const LazyNuxtWelcome: LazyComponent +export const LazyNuxtLayout: LazyComponent +export const LazyNuxtErrorBoundary: LazyComponent +export const LazyClientOnly: LazyComponent +export const LazyDevOnly: LazyComponent +export const LazyServerPlaceholder: LazyComponent +export const LazyNuxtLink: LazyComponent +export const LazyNuxtLoadingIndicator: LazyComponent +export const LazyNuxtTime: LazyComponent +export const LazyNuxtRouteAnnouncer: LazyComponent +export const LazyNuxtAnnouncer: LazyComponent +export const LazyNuxtImg: LazyComponent +export const LazyNuxtPicture: LazyComponent +export const LazyNuxtPage: LazyComponent +export const LazyNoScript: LazyComponent +export const LazyLink: LazyComponent +export const LazyBase: LazyComponent +export const LazyTitle: LazyComponent +export const LazyMeta: LazyComponent +export const LazyStyle: LazyComponent +export const LazyHead: LazyComponent +export const LazyHtml: LazyComponent +export const LazyBody: LazyComponent +export const LazyIcon: LazyComponent +export const LazyColorScheme: LazyComponent +export const LazyNuxtIsland: LazyComponent + +export const componentNames: string[] diff --git a/packages/devtools-app/.nuxt/imports.d.ts b/packages/devtools-app/.nuxt/imports.d.ts new file mode 100644 index 000000000..67ea0eb79 --- /dev/null +++ b/packages/devtools-app/.nuxt/imports.d.ts @@ -0,0 +1,49 @@ +export { useScriptTriggerConsent, useScriptEventPage, useScriptTriggerElement, useScript, useScriptGoogleAnalytics, useScriptPlausibleAnalytics, useScriptCrisp, useScriptClarity, useScriptCloudflareWebAnalytics, useScriptFathomAnalytics, useScriptMatomoAnalytics, useScriptGoogleTagManager, useScriptGoogleAdsense, useScriptSegment, useScriptMetaPixel, useScriptXPixel, useScriptIntercom, useScriptHotjar, useScriptStripe, useScriptLemonSqueezy, useScriptVimeoPlayer, useScriptYouTubePlayer, useScriptGoogleMaps, useScriptNpm, useScriptUmamiAnalytics, useScriptSnapchatPixel, useScriptRybbitAnalytics, useScriptDatabuddyAnalytics, useScriptRedditPixel, useScriptPayPal } from '#app/composables/script-stubs'; +export { isVue2, isVue3 } from 'vue-demi'; +export { defineNuxtLink } from '#app/components/nuxt-link'; +export { useNuxtApp, tryUseNuxtApp, defineNuxtPlugin, definePayloadPlugin, useRuntimeConfig, defineAppConfig } from '#app/nuxt'; +export { useAppConfig, updateAppConfig } from '#app/config'; +export { defineNuxtComponent } from '#app/composables/component'; +export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, createUseAsyncData } from '#app/composables/asyncData'; +export { useHydration } from '#app/composables/hydrate'; +export { callOnce } from '#app/composables/once'; +export { useState, clearNuxtState } from '#app/composables/state'; +export { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error'; +export { useFetch, useLazyFetch, createUseFetch } from '#app/composables/fetch'; +export { useCookie, refreshCookie } from '#app/composables/cookie'; +export { onPrehydrate, prerenderRoutes, useRequestHeader, useRequestHeaders, useResponseHeader, useRequestEvent, useRequestFetch, setResponseStatus } from '#app/composables/ssr'; +export { onNuxtReady } from '#app/composables/ready'; +export { preloadComponents, prefetchComponents, preloadRouteComponents } from '#app/composables/preload'; +export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter } from '#app/composables/router'; +export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from '#app/composables/payload'; +export { useLoadingIndicator } from '#app/composables/loading-indicator'; +export { getAppManifest, getRouteRules } from '#app/composables/manifest'; +export { reloadNuxtApp } from '#app/composables/chunk'; +export { useRequestURL } from '#app/composables/url'; +export { usePreviewMode } from '#app/composables/preview'; +export { useRouteAnnouncer } from '#app/composables/route-announcer'; +export { useAnnouncer } from '#app/composables/announcer'; +export { useRuntimeHook } from '#app/composables/runtime-hook'; +export { useHead, useHeadSafe, useServerHeadSafe, useServerHead, useSeoMeta, useServerSeoMeta, injectHead } from '#app/composables/head'; +export { onBeforeRouteLeave, onBeforeRouteUpdate, useLink } from 'vue-router'; +export { withCtx, withDirectives, withKeys, withMemo, withModifiers, withScopeId, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onServerPrefetch, onUnmounted, onUpdated, computed, customRef, isProxy, isReactive, isReadonly, isRef, markRaw, proxyRefs, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, watch, watchEffect, watchPostEffect, watchSyncEffect, onWatcherCleanup, isShallow, effect, effectScope, getCurrentScope, onScopeDispose, defineComponent, defineAsyncComponent, resolveComponent, getCurrentInstance, h, inject, hasInjectionContext, nextTick, provide, toValue, useModel, useAttrs, useCssModule, useCssVars, useSlots, useTransitionState, useId, useTemplateRef, useShadowRoot, Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'; +export { requestIdleCallback, cancelIdleCallback } from '#app/compat/idle-callback'; +export { setInterval } from '#app/compat/interval'; +export { computedAsync, asyncComputed, computedEager, eagerComputed, computedInject, computedWithControl, controlledComputed, createEventHook, createGlobalState, createInjectionState, createRef, createReusableTemplate, createSharedComposable, createTemplatePromise, createUnrefFn, extendRef, injectLocal, isDefined, makeDestructurable, onClickOutside, onElementRemoval, onKeyStroke, onLongPress, onStartTyping, provideLocal, reactify, createReactiveFn, reactifyObject, reactiveComputed, reactiveOmit, reactivePick, refAutoReset, autoResetRef, refDebounced, useDebounce, debouncedRef, refDefault, refManualReset, refThrottled, useThrottle, throttledRef, refWithControl, controlledRef, syncRef, syncRefs, templateRef, toReactive, tryOnBeforeMount, tryOnBeforeUnmount, tryOnMounted, tryOnScopeDispose, tryOnUnmounted, unrefElement, until, useActiveElement, useAnimate, useArrayDifference, useArrayEvery, useArrayFilter, useArrayFind, useArrayFindIndex, useArrayFindLast, useArrayIncludes, useArrayJoin, useArrayMap, useArrayReduce, useArraySome, useArrayUnique, useAsyncQueue, useAsyncState, useBase64, useBattery, useBluetooth, useBreakpoints, useBroadcastChannel, useBrowserLocation, useCached, useClipboard, useClipboardItems, useCloned, useConfirmDialog, useCountdown, useCounter, useCssSupports, useCssVar, useCurrentElement, useCycleList, useDark, useDateFormat, useDebouncedRefHistory, useDebounceFn, useDeviceMotion, useDeviceOrientation, useDevicePixelRatio, useDevicesList, useDisplayMedia, useDocumentVisibility, useDraggable, useDropZone, useElementBounding, useElementByPoint, useElementHover, useElementSize, useElementVisibility, useEventBus, useEventListener, useEventSource, useEyeDropper, useFavicon, useFileDialog, useFileSystemAccess, useFocus, useFocusWithin, useFps, useFullscreen, useGamepad, useGeolocation, useIdle, useInfiniteScroll, useIntersectionObserver, useInterval, useIntervalFn, useKeyModifier, useLastChanged, useLocalStorage, useMagicKeys, useManualRefHistory, useMediaControls, useMediaQuery, useMemoize, useMemory, useMounted, useMouse, useMouseInElement, useMousePressed, useMutationObserver, useNavigatorLanguage, useNetwork, useNow, useObjectUrl, useOffsetPagination, useOnline, usePageLeave, useParallax, useParentElement, usePerformanceObserver, usePermission, usePointer, usePointerLock, usePointerSwipe, usePreferredColorScheme, usePreferredContrast, usePreferredDark, usePreferredLanguages, usePreferredReducedMotion, usePreferredReducedTransparency, usePrevious, useRafFn, useRefHistory, useResizeObserver, useScreenOrientation, useScreenSafeArea, useScriptTag, useScroll, useScrollLock, useSessionStorage, useShare, useSorted, useSpeechRecognition, useSpeechSynthesis, useSSRWidth, useStepper, useStorageAsync, useStyleTag, useSupported, useSwipe, useTemplateRefsList, useTextareaAutosize, useTextDirection, useTextSelection, useThrottledRefHistory, useThrottleFn, useTimeAgo, useTimeAgoIntl, useTimeout, useTimeoutFn, useTimeoutPoll, useTimestamp, useTitle, useToggle, useToNumber, useToString, useTransition, useUrlSearchParams, useUserMedia, useVibrate, useVirtualList, useVModel, useVModels, useWakeLock, useWebNotification, useWebSocket, useWebWorker, useWebWorkerFn, useWindowFocus, useWindowScroll, useWindowSize, watchArray, watchAtMost, watchDebounced, debouncedWatch, watchDeep, watchIgnorable, ignorableWatch, watchImmediate, watchOnce, watchPausable, pausableWatch, watchThrottled, throttledWatch, watchTriggerable, watchWithFilter, whenever } from '@vueuse/core'; +export { definePageMeta, PageMeta } from '#app/composables/pages'; +export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration'; +export { colorMode, useDevtoolsConnection } from '../composables/rpc'; +export { state, isConnected, syncState, SeoOverview, SerializedEntry, SerializedTag, SerializedScript, SerializedValidationRule, UnheadDevtoolsState } from '../composables/state'; +export { SEO_LIMITS, estimatePixelWidth, titleColor, descColor } from '../composables/tools'; +export { defineLocale, extendLocale } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale'; +export { defineShortcuts, extractShortcuts } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts'; +export { useContentSearch } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch'; +export { useFileUpload } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload'; +export { useFormField } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useFormField'; +export { useKbd } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useKbd'; +export { useOverlay } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay'; +export { useResizable } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useResizable'; +export { useScrollShadow } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useScrollShadow'; +export { useScrollspy } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy'; +export { useToast } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useToast'; +export { useColorMode } from '../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/composables'; \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json b/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json b/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json b/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json b/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json b/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json b/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json b/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt-fonts-global.css b/packages/devtools-app/.nuxt/nuxt-fonts-global.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs b/packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs new file mode 100644 index 000000000..8f785f931 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt-icon-client-bundle.mjs @@ -0,0 +1 @@ +export function init() {} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs b/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs new file mode 100644 index 000000000..95287ba80 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs @@ -0,0 +1,13 @@ +function createRemoteCollection(fetchEndpoint) { + let _cache + return async () => { + if (_cache) + return _cache + const res = await fetch(fetchEndpoint).then(r => r.json()) + _cache = res + return res + } +} + +export const collections = { +} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt.d.ts b/packages/devtools-app/.nuxt/nuxt.d.ts new file mode 100644 index 000000000..dfcccabfd --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt.d.ts @@ -0,0 +1,24 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +export {} diff --git a/packages/devtools-app/.nuxt/nuxt.node.d.ts b/packages/devtools-app/.nuxt/nuxt.node.d.ts new file mode 100644 index 000000000..ae6e2dec4 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt.node.d.ts @@ -0,0 +1,14 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +export {} diff --git a/packages/devtools-app/.nuxt/nuxt.shared.d.ts b/packages/devtools-app/.nuxt/nuxt.shared.d.ts new file mode 100644 index 000000000..812815770 --- /dev/null +++ b/packages/devtools-app/.nuxt/nuxt.shared.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// +/// + +export {} diff --git a/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs b/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs new file mode 100644 index 000000000..f8de1bfd0 --- /dev/null +++ b/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs @@ -0,0 +1,19 @@ +import { escapeHtml } from 'file:///home/harlan/pkg/unhead/.claude/worktrees/feat-vite-devtools/node_modules/.pnpm/@vue+shared@3.5.32/node_modules/@vue/shared/dist/shared.cjs.prod.js'; + +const _messages = { + "appName": "Nuxt", + "status": 500, + "statusText": "Internal server error", + "description": "This page is temporarily unavailable.", + "refresh": "Refresh this page" +}; +const template = (messages) => { + messages = { + ..._messages, + ...messages + }; + return "" + escapeHtml(messages.status) + " - " + escapeHtml(messages.statusText) + " | " + escapeHtml(messages.appName) + " + + + + diff --git a/packages/devtools-app/assets/css/global.css b/packages/devtools-app/assets/css/global.css new file mode 100644 index 000000000..747deb497 --- /dev/null +++ b/packages/devtools-app/assets/css/global.css @@ -0,0 +1,27 @@ +@import "tailwindcss"; +@import "@nuxt/ui"; + +@theme { + --font-sans: 'Hubot Sans', ui-sans-serif, system-ui, sans-serif; + --font-mono: 'Fira Code', ui-monospace, monospace; +} + +html, body { + margin: 0; + padding: 0; + font-size: 14px; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-thumb { + background: var(--ui-border); + border-radius: 3px; +} + +::-webkit-scrollbar-track { + background: transparent; +} diff --git a/packages/devtools-app/composables/rpc.ts b/packages/devtools-app/composables/rpc.ts new file mode 100644 index 000000000..f2d13e7d5 --- /dev/null +++ b/packages/devtools-app/composables/rpc.ts @@ -0,0 +1,33 @@ +import type { UnheadDevtoolsState } from './state' +import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client' +import { isConnected, syncState } from './state' + +export const colorMode = ref<'dark' | 'light'>('dark') + +export async function useDevtoolsConnection(): Promise { + if (typeof window === 'undefined') + return + + console.log('[unhead devtools-app] useDevtoolsConnection: getting RPC client') + const client = await getDevToolsRpcClient() + console.log('[unhead devtools-app] RPC client obtained:', !!client) + + const sharedState = await (client.sharedState as any).get('unhead:state') + console.log('[unhead devtools-app] sharedState handle obtained') + + const current = sharedState.value() as UnheadDevtoolsState | null + console.log('[unhead devtools-app] initial value:', current ? `${current.tags.length} tags, ${current.entries.length} entries` : 'null') + + if (current) { + isConnected.value = true + syncState(current) + } + + sharedState.on('updated', (newState: UnheadDevtoolsState) => { + console.log('[unhead devtools-app] sharedState updated:', newState ? `${newState.tags.length} tags, ${newState.entries.length} entries` : 'null') + if (newState) { + isConnected.value = true + syncState(newState) + } + }) +} diff --git a/packages/devtools-app/composables/state.ts b/packages/devtools-app/composables/state.ts new file mode 100644 index 000000000..6bd7d032d --- /dev/null +++ b/packages/devtools-app/composables/state.ts @@ -0,0 +1,85 @@ +import { ref } from 'vue' + +export interface SeoOverview { + title: string + description: string + canonical: string + robots: string + ogTitle: string + ogDescription: string + ogImage: string +} + +export interface SerializedEntry { + id: number + source?: string + input: Record + tagCount: number +} + +export interface SerializedTag { + tag: string + props: Record + innerHTML?: string + textContent?: string + position?: string + priority?: number + dedupeKey?: string + source?: string +} + +export interface SerializedScript { + id: string + src: string + status: string +} + +export interface SerializedValidationRule { + id: string + message: string + severity: 'warn' | 'info' + source?: string +} + +export interface UnheadDevtoolsState { + entries: SerializedEntry[] + tags: SerializedTag[] + plugins: string[] + title: string + scripts: SerializedScript[] + seo: SeoOverview + titleTemplate: string | null + templateParams: Record | null + separator: string + ssr: boolean + dirty: boolean + domElementCount: number + tagTypeCounts: Record + validationRules: SerializedValidationRule[] +} + +const defaultState: UnheadDevtoolsState = { + entries: [], + tags: [], + plugins: [], + title: '', + scripts: [], + seo: { title: '', description: '', canonical: '', robots: '', ogTitle: '', ogDescription: '', ogImage: '' }, + titleTemplate: null, + templateParams: null, + separator: '|', + ssr: false, + dirty: false, + domElementCount: 0, + tagTypeCounts: {}, + validationRules: [], +} + +export const state = ref({ ...defaultState }) +export const isConnected = ref(false) + +export function syncState(newState: UnheadDevtoolsState) { + if (!newState) + return + state.value = newState +} diff --git a/packages/devtools-app/composables/tools.ts b/packages/devtools-app/composables/tools.ts new file mode 100644 index 000000000..bf419ab09 --- /dev/null +++ b/packages/devtools-app/composables/tools.ts @@ -0,0 +1,28 @@ +export const SEO_LIMITS = { + TITLE_MAX_CHARS: 60, + TITLE_WARN_CHARS: 50, + TITLE_MAX_PIXELS: 580, + DESC_MAX_CHARS: 160, + DESC_WARN_CHARS: 150, + DESC_MAX_PIXELS: 920, +} as const + +export function estimatePixelWidth(text: string, fontSize: number = 16): number { + return Math.round(text.length * fontSize * 0.55) +} + +export function titleColor(length: number) { + if (length > SEO_LIMITS.TITLE_MAX_CHARS) + return 'error' + if (length < 30) + return 'warning' + return 'success' +} + +export function descColor(length: number) { + if (length > SEO_LIMITS.DESC_MAX_CHARS) + return 'error' + if (length > SEO_LIMITS.DESC_WARN_CHARS || length < 70) + return 'warning' + return 'success' +} diff --git a/packages/devtools-app/nuxt.config.ts b/packages/devtools-app/nuxt.config.ts new file mode 100644 index 000000000..b4ecce47c --- /dev/null +++ b/packages/devtools-app/nuxt.config.ts @@ -0,0 +1,51 @@ +import { resolve } from 'pathe' + +export default defineNuxtConfig({ + ssr: false, + + modules: [ + '@nuxt/ui', + '@vueuse/nuxt', + ], + + css: [resolve(__dirname, 'assets/css/global.css')], + + imports: { + autoImport: true, + }, + + devtools: { + enabled: false, + }, + + compatibilityDate: '2026-03-13', + + nitro: { + prerender: { + crawlLinks: false, + routes: ['/'], + failOnError: false, + }, + output: { + publicDir: resolve(__dirname, 'dist'), + }, + }, + + vite: { + optimizeDeps: { + include: [ + '@vueuse/core', + ], + exclude: ['jiti'], + }, + resolve: { + alias: { + jiti: 'data:text/javascript,export default () => {}', + }, + }, + }, + + app: { + baseURL: '/__unhead/', + }, +}) diff --git a/packages/devtools-app/package.json b/packages/devtools-app/package.json new file mode 100644 index 000000000..36f14f422 --- /dev/null +++ b/packages/devtools-app/package.json @@ -0,0 +1,20 @@ +{ + "name": "@unhead/devtools-app", + "type": "module", + "private": true, + "scripts": { + "dev": "nuxi dev", + "build": "nuxi generate", + "generate": "nuxi generate" + }, + "devDependencies": { + "@nuxt/ui": "catalog:conflicts_@nuxt/ui_h4_6_1", + "@unhead/vue": "workspace:*", + "@vitejs/devtools-kit": "catalog:", + "@vueuse/nuxt": "catalog:conflicts_conflicts_@vueuse/nuxt_h14_h14_2_1", + "nuxt": "catalog:conflicts_nuxt_h4_4_2", + "pathe": "catalog:conflicts_pathe_h2", + "tailwindcss": "catalog:", + "vue": "catalog:" + } +} diff --git a/packages/devtools-app/pages/identity.vue b/packages/devtools-app/pages/identity.vue new file mode 100644 index 000000000..b40e199d4 --- /dev/null +++ b/packages/devtools-app/pages/identity.vue @@ -0,0 +1,475 @@ + + + + + diff --git a/packages/devtools-app/pages/index.vue b/packages/devtools-app/pages/index.vue new file mode 100644 index 000000000..641710234 --- /dev/null +++ b/packages/devtools-app/pages/index.vue @@ -0,0 +1,208 @@ + + + diff --git a/packages/devtools-app/pages/schema.vue b/packages/devtools-app/pages/schema.vue new file mode 100644 index 000000000..910af8d94 --- /dev/null +++ b/packages/devtools-app/pages/schema.vue @@ -0,0 +1,244 @@ + + + diff --git a/packages/devtools-app/pages/scripts.vue b/packages/devtools-app/pages/scripts.vue new file mode 100644 index 000000000..bebcea4f7 --- /dev/null +++ b/packages/devtools-app/pages/scripts.vue @@ -0,0 +1,106 @@ + + + diff --git a/packages/devtools-app/pages/serp.vue b/packages/devtools-app/pages/serp.vue new file mode 100644 index 000000000..e884d9bb7 --- /dev/null +++ b/packages/devtools-app/pages/serp.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/packages/devtools-app/tsconfig.json b/packages/devtools-app/tsconfig.json new file mode 100644 index 000000000..4b34df157 --- /dev/null +++ b/packages/devtools-app/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/packages/devtools-app/utils/schema-validation.ts b/packages/devtools-app/utils/schema-validation.ts new file mode 100644 index 000000000..168011666 --- /dev/null +++ b/packages/devtools-app/utils/schema-validation.ts @@ -0,0 +1,470 @@ +// Rich result schema types that Google supports +export const richResultTypes = new Set([ + 'Article', + 'NewsArticle', + 'BlogPosting', + 'ScholarlyArticle', + 'Product', + 'AggregateOffer', + 'Offer', + 'FAQPage', + 'Question', + 'HowTo', + 'HowToStep', + 'Recipe', + 'Event', + 'LocalBusiness', + 'Restaurant', + 'JobPosting', + 'Course', + 'Movie', + 'Book', + 'SoftwareApplication', + 'VideoObject', + 'Review', + 'AggregateRating', + 'BreadcrumbList', + 'SearchAction', + 'Dataset', + 'SpecialAnnouncement', + 'Person', + 'NewsMediaOrganization', + 'Organization', +]) + +// Google Rich Results requirements for each schema type +export const googleRichResultsRequirements: Record = { + Article: { + required: [], + recommended: ['author', 'author.name', 'dateModified', 'datePublished', 'headline', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/article', + }, + NewsArticle: { + required: [], + recommended: ['author', 'author.name', 'dateModified', 'datePublished', 'headline', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/article', + }, + BlogPosting: { + required: [], + recommended: ['author', 'author.name', 'dateModified', 'datePublished', 'headline', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/article', + }, + Product: { + required: ['name'], + recommended: ['description', 'offers', 'offers.price', 'offers.priceCurrency', 'offers.availability', 'aggregateRating', 'review', 'image'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/product', + }, + FAQPage: { + required: ['mainEntity'], + recommended: [], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/faqpage', + }, + Recipe: { + required: ['name', 'image'], + recommended: ['aggregateRating', 'author', 'cookTime', 'datePublished', 'description', 'keywords', 'nutrition', 'prepTime', 'recipeCategory', 'recipeCuisine', 'recipeIngredient', 'recipeInstructions', 'recipeYield', 'totalTime', 'video'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/recipe', + }, + Event: { + required: ['name', 'location', 'startDate'], + recommended: ['description', 'endDate', 'eventStatus', 'image', 'offers', 'performer', 'organizer'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/event', + }, + LocalBusiness: { + required: ['name', 'address'], + recommended: ['aggregateRating', 'department', 'geo', 'openingHoursSpecification', 'priceRange', 'telephone', 'url'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/local-business', + }, + Restaurant: { + required: ['name', 'address'], + recommended: ['aggregateRating', 'servesCuisine', 'hasMenu', 'geo', 'openingHoursSpecification', 'priceRange', 'telephone', 'url'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/local-business', + }, + Review: { + required: ['author', 'itemReviewed', 'reviewRating'], + recommended: ['datePublished', 'reviewRating.bestRating', 'reviewRating.worstRating'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/review-snippet', + }, + AggregateRating: { + required: ['itemReviewed', 'ratingValue'], + recommended: ['bestRating', 'worstRating', 'ratingCount', 'reviewCount'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/review-snippet', + }, + BreadcrumbList: { + required: ['itemListElement'], + recommended: [], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/breadcrumb', + }, + Organization: { + required: [], + recommended: ['name', 'logo', 'url', 'email', 'telephone', 'contactPoint', 'sameAs', 'address', 'description'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/organization', + }, + Person: { + required: [], + recommended: ['name', 'url', 'image', 'sameAs', 'jobTitle', 'worksFor'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/person', + }, + SoftwareApplication: { + required: ['name', 'offers'], + recommended: ['applicationCategory', 'operatingSystem', 'aggregateRating', 'review'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/software-app', + }, + VideoObject: { + required: ['name', 'thumbnailUrl', 'uploadDate'], + recommended: ['contentUrl', 'description', 'duration', 'embedUrl'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/video', + }, + JobPosting: { + required: ['datePosted', 'description', 'hiringOrganization', 'jobLocation', 'title'], + recommended: ['applicantLocationRequirements', 'baseSalary', 'employmentType', 'jobLocationType', 'validThrough'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/job-posting', + }, + Course: { + required: ['description', 'name'], + recommended: ['provider'], + documentationUrl: 'https://developers.google.com/search/docs/appearance/structured-data/course', + }, +} + +// Recursively extract all typed objects from a schema node +function extractNestedTypes(obj: any, nodes: any[], seen: Set): void { + if (!obj || typeof obj !== 'object' || seen.has(obj)) + return + seen.add(obj) + + if (obj['@type'] && Object.keys(obj).length > 1) { + nodes.push(obj) + } + + for (const value of Object.values(obj)) { + if (Array.isArray(value)) { + for (const item of value) { + extractNestedTypes(item, nodes, seen) + } + } + else if (typeof value === 'object') { + extractNestedTypes(value, nodes, seen) + } + } +} + +// Resolve @id references in a node +function resolveNodeReferences(node: any, nodeMap: Map): any { + const resolved = { ...node } + + Object.keys(resolved).forEach((key) => { + const value = resolved[key] + + if ( + value + && typeof value === 'object' + && !Array.isArray(value) + && Object.keys(value).length === 1 + && value['@id'] + && nodeMap.has(value['@id']) + ) { + resolved[key] = { ...nodeMap.get(value['@id']) } + } + else if (Array.isArray(value)) { + resolved[key] = value.map((item: any) => { + if ( + item + && typeof item === 'object' + && Object.keys(item).length === 1 + && item['@id'] + && nodeMap.has(item['@id']) + ) { + return { ...nodeMap.get(item['@id']) } + } + return item + }) + } + else if (value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 1) { + resolved[key] = resolveNodeReferences(value, nodeMap) + } + }) + + return resolved +} + +// Extract all nodes from a schema graph (including nested @graph and nested types) +export function extractSchemaNodes(data: any): any[] { + const nodes: any[] = [] + if (!data) + return nodes + + const seen = new Set() + + if (data['@graph'] && Array.isArray(data['@graph'])) { + const nodeMap = new Map() + data['@graph'].forEach((node: any) => { + if (node && typeof node === 'object' && node['@id']) { + nodeMap.set(node['@id'], node) + } + }) + + const addedIds = new Set() + + data['@graph'].forEach((node: any) => { + if (node && typeof node === 'object') { + if (Object.keys(node).length === 1 && node['@id']) + return + if (node['@id'] && addedIds.has(node['@id'])) + return + + const resolvedNode = resolveNodeReferences(node, nodeMap) + extractNestedTypes(resolvedNode, nodes, seen) + + if (node['@id']) + addedIds.add(node['@id']) + } + }) + } + else if (data['@type']) { + extractNestedTypes(data, nodes, seen) + } + + // Deduplicate nodes that appear multiple times due to @id reference resolution + const uniqueNodes: any[] = [] + const seenIds = new Set() + for (const node of nodes) { + const id = node['@id'] + if (id) { + if (seenIds.has(id)) + continue + seenIds.add(id) + } + uniqueNodes.push(node) + } + + return uniqueNodes +} + +export function getNodeType(node: any): string { + if (!node || !node['@type']) + return 'Unknown' + return Array.isArray(node['@type']) ? node['@type'][0] : node['@type'] +} + +export function isRichResultType(type: string): boolean { + return richResultTypes.has(type) +} + +export function getNodeDescription(node: any): string { + const type = getNodeType(node) + + if (node.name) + return node.name + if (node.headline) + return node.headline + if (node.title) + return node.title + if (node.description) { + return typeof node.description === 'string' + ? node.description.substring(0, 100) + (node.description.length > 100 ? '...' : '') + : '' + } + if (node['@id']) + return node['@id'] + if (node.url) + return node.url + + return `${type} Schema` +} + +export function getNestedProperty(obj: any, path: string): any { + const parts = path.split('.') + let current = obj + + for (const part of parts) { + if (current && typeof current === 'object' && part in current) { + current = current[part] + } + else { + return undefined + } + } + + return current +} + +export function analyzeNodeProperties(node: any): { + missingRequired: string[] + missingRecommended: string[] + presentProperties: Record +} { + const type = getNodeType(node) + const requirements = googleRichResultsRequirements[type] + + if (!requirements) { + return { + missingRequired: [], + missingRecommended: [], + presentProperties: {}, + } + } + + const missingRequired: string[] = [] + const missingRecommended: string[] = [] + const presentProperties: Record = {} + + requirements.required.forEach((prop) => { + const value = getNestedProperty(node, prop) + if (value === undefined || value === null || value === '') { + missingRequired.push(prop) + } + else { + presentProperties[prop] = value + } + }) + + requirements.recommended.forEach((prop) => { + const value = getNestedProperty(node, prop) + if (value === undefined || value === null || value === '') { + missingRecommended.push(prop) + } + else { + presentProperties[prop] = value + } + }) + + return { missingRequired, missingRecommended, presentProperties } +} + +export function formatPropertyValue(value: any): string { + if (value === null || value === undefined) + return 'null' + if (typeof value === 'string') + return value.length > 50 ? `${value.substring(0, 50)}...` : value + if (typeof value === 'number') + return value.toString() + if (typeof value === 'boolean') + return value ? 'true' : 'false' + if (Array.isArray(value)) + return `[${value.length} items]` + if (typeof value === 'object' && value['@type']) + return value['@type'] + if (typeof value === 'object') + return '{...}' + return String(value) +} + +export function getSchemaIcon(type: string): string { + const iconMap: Record = { + Article: 'carbon:document', + NewsArticle: 'carbon:news', + BlogPosting: 'carbon:blog', + Product: 'carbon:shopping-cart', + FAQPage: 'carbon:help', + Organization: 'carbon:building', + LocalBusiness: 'carbon:location', + Person: 'carbon:user', + Event: 'carbon:calendar', + SoftwareApplication: 'carbon:application', + Recipe: 'carbon:restaurant', + HowTo: 'carbon:list-numbered', + WebSite: 'carbon:earth', + WebPage: 'carbon:page-first', + BreadcrumbList: 'carbon:flow', + VideoObject: 'carbon:video', + Review: 'carbon:star', + AggregateRating: 'carbon:star-filled', + SearchAction: 'carbon:search', + } + return iconMap[type] || 'carbon:code' +} + +export interface ValidationSummary { + totalNodes: number + richResultNodes: number + totalErrors: number + totalWarnings: number +} + +// Google structured data page slugs mapped to schema types +export const googleStructuredDataLinks: Record = { + 'article': ['Article', 'NewsArticle', 'BlogPosting'], + 'book': ['Book'], + 'breadcrumb': ['BreadcrumbList'], + 'carousel': ['ItemList'], + 'course-info': ['Course'], + 'course': ['Course'], + 'dataset': ['Dataset'], + 'discussion-forum': ['DiscussionForumPosting'], + 'education-qa': ['Question', 'Answer'], + 'employer-rating': ['EmployerAggregateRating'], + 'estimated-salary': ['OccupationalExperienceRequirements'], + 'event': ['Event'], + 'factcheck': ['ClaimReview'], + 'faqpage': ['FAQPage'], + 'image-license-metadata': ['ImageObject'], + 'job-posting': ['JobPosting'], + 'learning-video': ['LearningResource', 'VideoObject'], + 'local-business': ['LocalBusiness'], + 'math-solvers': ['MathSolver'], + 'movie': ['Movie'], + 'organization': ['Organization'], + 'practice-problems': ['Quiz', 'Question'], + 'product': ['Product'], + 'product-snippet': ['Product'], + 'merchant-listing': ['Product', 'Offer'], + 'product-variants': ['Product'], + 'profile-page': ['ProfilePage', 'Person'], + 'qapage': ['QAPage'], + 'recipe': ['Recipe'], + 'review-snippet': ['Review'], + 'software-app': ['SoftwareApplication'], + 'speakable': ['SpeakableSpecification'], + 'special-announcements': ['SpecialAnnouncement'], + 'paywalled-content': ['CreativeWork'], + 'vacation-rental': ['Accommodation', 'LodgingBusiness'], + 'vehicle-listing': ['Vehicle'], + 'video': ['VideoObject'], +} + +export function nodeToSchemaOrgLink(type: string) { + const simpleType = type.replace('https://schema.org/', '') + const googlePage = Object.entries(googleStructuredDataLinks) + .find(([_, types]) => types.includes(simpleType))?.[0] + return { + type: simpleType, + schemaOrg: `https://schema.org/${simpleType}`, + googlePage: googlePage ? `https://developers.google.com/search/docs/appearance/structured-data/${googlePage}` : null, + } +} + +export function asArray(value: T | T[]): T[] { + return Array.isArray(value) ? value : [value] +} + +export function validateGraph(data: any): { nodes: any[], summary: ValidationSummary } { + const nodes = extractSchemaNodes(data) + + let totalErrors = 0 + let totalWarnings = 0 + let richResultNodes = 0 + + for (const node of nodes) { + const type = getNodeType(node) + if (isRichResultType(type)) + richResultNodes++ + + const analysis = analyzeNodeProperties(node) + totalErrors += analysis.missingRequired.length + totalWarnings += analysis.missingRecommended.length + } + + return { + nodes, + summary: { + totalNodes: nodes.length, + richResultNodes, + totalErrors, + totalWarnings, + }, + } +} diff --git a/packages/devtools/build.config.ts b/packages/devtools/build.config.ts new file mode 100644 index 000000000..9cb67a34f --- /dev/null +++ b/packages/devtools/build.config.ts @@ -0,0 +1,18 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + clean: true, + declaration: true, + entries: [ + { input: 'src/index', name: 'index' }, + { input: 'src/vite', name: 'vite' }, + { input: 'src/plugin', name: 'plugin' }, + { input: 'src/bridge', name: 'bridge' }, + ], + externals: [ + 'vite', + 'unhead', + '@vitejs/devtools-kit', + '@vitejs/devtools-kit/client', + ], +}) diff --git a/packages/devtools/package.json b/packages/devtools/package.json new file mode 100644 index 000000000..4c66e2cb8 --- /dev/null +++ b/packages/devtools/package.json @@ -0,0 +1,70 @@ +{ + "name": "@unhead/devtools", + "type": "module", + "version": "3.0.0-beta.12", + "description": "Vite DevTools integration for Unhead.", + "author": "Harlan Wilton ", + "license": "MIT", + "funding": "https://github.com/sponsors/harlan-zw", + "homepage": "https://unhead.unjs.io", + "repository": { + "type": "git", + "url": "git+https://github.com/unjs/unhead.git", + "directory": "packages/devtools" + }, + "publishConfig": { + "access": "public", + "tag": "next" + }, + "bugs": { + "url": "https://github.com/unjs/unhead/issues" + }, + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "./vite": { + "types": "./dist/vite.d.ts", + "default": "./dist/vite.mjs" + }, + "./plugin": { + "types": "./dist/plugin.d.ts", + "default": "./dist/plugin.mjs" + }, + "./bridge": { + "types": "./dist/bridge.d.ts", + "default": "./dist/bridge.mjs" + } + }, + "main": "dist/index.mjs", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "unbuild", + "stub": "unbuild --stub" + }, + "peerDependencies": { + "vite": ">=6.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + }, + "dependencies": { + "magic-string": "catalog:", + "oxc-parser": "catalog:", + "oxc-walker": "catalog:", + "unhead": "workspace:*" + }, + "devDependencies": { + "@vitejs/devtools-kit": "catalog:", + "unbuild": "catalog:", + "vite": "catalog:" + } +} diff --git a/packages/devtools/src/bridge.ts b/packages/devtools/src/bridge.ts new file mode 100644 index 000000000..df30d887a --- /dev/null +++ b/packages/devtools/src/bridge.ts @@ -0,0 +1,240 @@ +import type { SeoOverview, SerializedScript, SerializedTag, SerializedValidationRule, UnheadDevtoolsState } from './rpc/types' + +declare global { + interface Window { + __unhead__?: { + _head?: any + _q?: any[] + push?: (e: any) => void + } + __unhead_devtools__?: any + } +} + +function extractSeoOverview(tags: SerializedTag[], title: string): SeoOverview { + const seo: SeoOverview = { + title, + description: '', + canonical: '', + robots: '', + ogTitle: '', + ogDescription: '', + ogImage: '', + } + for (const t of tags) { + if (t.tag === 'meta') { + const name = t.props.name + const property = t.props.property + const content = t.props.content || '' + if (name === 'description') + seo.description = content + else if (name === 'robots') + seo.robots = content + else if (property === 'og:title') + seo.ogTitle = content + else if (property === 'og:description') + seo.ogDescription = content + else if (property === 'og:image') + seo.ogImage = content + } + else if (t.tag === 'link' && t.props.rel === 'canonical') { + seo.canonical = t.props.href || '' + } + } + return seo +} + +function serializeHeadState(head: any): UnheadDevtoolsState { + const entries: UnheadDevtoolsState['entries'] = [] + const allTags: UnheadDevtoolsState['tags'] = [] + const tagTypeCounts: Record = {} + + if (head.entries) { + for (const [id, entry] of head.entries) { + const tags = entry._tags || [] + entries.push({ + id, + source: entry.options?._source, + input: JSON.parse(JSON.stringify(entry.input || {})), + tagCount: tags.length, + }) + for (const tag of tags) { + const tagName = tag.tag + tagTypeCounts[tagName] = (tagTypeCounts[tagName] || 0) + 1 + allTags.push({ + tag: tagName, + props: { ...tag.props }, + innerHTML: tag.innerHTML, + textContent: tag.textContent, + position: tag.tagPosition, + priority: tag._w, + dedupeKey: tag._d, + source: tag._source || entry.options?._source, + }) + } + } + } + + const plugins: string[] = [] + if (head.plugins) { + for (const [key] of head.plugins) { + plugins.push(key) + } + } + + const scripts: SerializedScript[] = [] + if (head._scripts) { + for (const [id, script] of Object.entries(head._scripts)) { + const s = script as any + scripts.push({ + id, + src: s.src || s.input?.src || '', + status: s.status || 'unknown', + }) + } + } + + let templateParams: Record | null = null + if (head._templateParams) { + try { + templateParams = JSON.parse(JSON.stringify(head._templateParams)) + } + catch {} + } + + const title = head._title || document.title || '' + + // Read validation rules stored by ValidatePlugin (if active) + const validationRules: SerializedValidationRule[] = (head._validationRules || []).map((r: any) => ({ + id: r.id, + message: r.message, + severity: r.severity, + source: r.source, + })) + + return { + entries, + tags: allTags, + plugins, + title, + scripts, + seo: extractSeoOverview(allTags, title), + titleTemplate: head._titleTemplate + ? (typeof head._titleTemplate === 'function' ? String(head._titleTemplate) : head._titleTemplate) + : null, + templateParams, + separator: head._separator || '|', + ssr: !!head.ssr, + dirty: !!head.dirty, + domElementCount: head._dom?._e?.size || 0, + tagTypeCounts, + validationRules, + } +} + +function connectBridge(head: any) { + let sharedState: any + + function syncToSharedState() { + if (!sharedState) { + console.log('[unhead bridge] syncToSharedState: no sharedState yet') + return + } + const newState = serializeHeadState(head) + console.log('[unhead bridge] syncing state:', newState.tags.length, 'tags,', newState.entries.length, 'entries') + sharedState.mutate((draft: any) => { + Object.assign(draft, newState) + }) + } + + async function init() { + console.log('[unhead bridge] init: importing devtools-kit/client') + const { getDevToolsClientContext } = await import('@vitejs/devtools-kit/client') + + // Retry until DevTools client context is available + let ctx = getDevToolsClientContext() + console.log('[unhead bridge] getDevToolsClientContext:', ctx ? 'found' : 'not ready') + if (!ctx) { + let retries = 0 + await new Promise((resolve) => { + const timer = globalThis.setInterval(() => { + ctx = getDevToolsClientContext() + if (ctx || ++retries > 50) { + globalThis.clearInterval(timer) + if (ctx) + console.log('[unhead bridge] context found after', retries, 'retries') + else console.warn('[unhead bridge] gave up waiting for DevTools context after 50 retries') + resolve() + } + }, 100) + }) + } + if (!ctx) { + console.warn('[unhead bridge] no DevTools client context, aborting') + return + } + + console.log('[unhead bridge] creating shared state') + sharedState = await ctx.rpc.sharedState.get('unhead:state', { + initialValue: serializeHeadState(head), + }) + console.log('[unhead bridge] shared state created, initial value set') + + if (head.hooks) { + head.hooks.hook('dom:rendered', syncToSharedState) + console.log('[unhead bridge] hooked dom:rendered') + } + + // Initial sync after a short delay to capture early entries + setTimeout(syncToSharedState, 500) + } + + init().catch((err) => { + console.error('[unhead bridge] init failed:', err) + }) +} + +function findHead(): any { + // 1. Streaming plugin pattern: window.__unhead__._head + if (window.__unhead__?._head) { + console.log('[unhead bridge] found head via __unhead__._head') + return window.__unhead__._head + } + + // 2. Devtools-exposed pattern: window.__unhead_devtools__ + if (window.__unhead_devtools__) { + console.log('[unhead bridge] found head via __unhead_devtools__') + return window.__unhead_devtools__ + } + + return null +} + +function pollForHead() { + console.log('[unhead bridge] polling for head instance...') + let attempts = 0 + const handle = globalThis.setInterval(() => { + const h = findHead() + if (h) { + globalThis.clearInterval(handle) + console.log('[unhead bridge] head found after', attempts, 'attempts') + connectBridge(h) + } + if (++attempts > 50) { + globalThis.clearInterval(handle) + console.warn('[unhead bridge] gave up polling for head after 50 attempts') + } + }, 100) +} + +console.log('[unhead bridge] bridge script loaded') +if (typeof window !== 'undefined') { + const head = findHead() + if (head) { + console.log('[unhead bridge] head found immediately') + connectBridge(head) + } + else { + pollForHead() + } +} diff --git a/packages/devtools/src/index.ts b/packages/devtools/src/index.ts new file mode 100644 index 000000000..bedf9f855 --- /dev/null +++ b/packages/devtools/src/index.ts @@ -0,0 +1,2 @@ +export { devtoolsPlugin } from './plugin' +export type { SeoOverview, SerializedEntry, SerializedScript, SerializedTag, SerializedValidationRule, UnheadDevtoolsState } from './rpc/types' diff --git a/packages/devtools/src/plugin.ts b/packages/devtools/src/plugin.ts new file mode 100644 index 000000000..10b212ff6 --- /dev/null +++ b/packages/devtools/src/plugin.ts @@ -0,0 +1,26 @@ +import type { HeadPluginInput } from 'unhead' + +/** + * Unhead plugin that propagates source location metadata from entry options to tags. + * Used in dev mode for devtools source tracing. + * + * When the Vite transform injects `_source` into `useHead()` options, this plugin + * ensures that metadata flows through to each resolved tag so the devtools can + * display which file:line created each tag. + */ +export function devtoolsPlugin(): HeadPluginInput { + return { + key: 'devtools', + hooks: { + 'entries:normalize': function ({ tags, entry }) { + const source = entry.options?._source + if (!source) + return + for (const tag of tags) { + if (!tag._source) + tag._source = source + } + }, + }, + } +} diff --git a/packages/devtools/src/rpc/functions/get-config.ts b/packages/devtools/src/rpc/functions/get-config.ts new file mode 100644 index 000000000..c01c445f1 --- /dev/null +++ b/packages/devtools/src/rpc/functions/get-config.ts @@ -0,0 +1,12 @@ +import { defineRpcFunction } from '@vitejs/devtools-kit' + +export const getConfigRpc = defineRpcFunction({ + name: 'unhead:get-config', + type: 'static', + setup: ctx => ({ + handler: () => ({ + cwd: ctx.cwd, + mode: ctx.mode, + }), + }), +}) diff --git a/packages/devtools/src/rpc/index.ts b/packages/devtools/src/rpc/index.ts new file mode 100644 index 000000000..581c788d1 --- /dev/null +++ b/packages/devtools/src/rpc/index.ts @@ -0,0 +1 @@ +export { getConfigRpc } from './functions/get-config' diff --git a/packages/devtools/src/rpc/types.ts b/packages/devtools/src/rpc/types.ts new file mode 100644 index 000000000..3e62d707d --- /dev/null +++ b/packages/devtools/src/rpc/types.ts @@ -0,0 +1,57 @@ +export interface SerializedEntry { + id: number + source?: string + input: Record + tagCount: number +} + +export interface SerializedTag { + tag: string + props: Record + innerHTML?: string + textContent?: string + position?: string + priority?: number + dedupeKey?: string + source?: string +} + +export interface SerializedScript { + id: string + src: string + status: string +} + +export interface SeoOverview { + title: string + description: string + canonical: string + robots: string + ogTitle: string + ogDescription: string + ogImage: string +} + +export interface SerializedValidationRule { + id: string + message: string + severity: 'warn' | 'info' + source?: string +} + +export interface UnheadDevtoolsState { + entries: SerializedEntry[] + tags: SerializedTag[] + plugins: string[] + title: string + scripts: SerializedScript[] + seo: SeoOverview + titleTemplate: string | null + templateParams: Record | null + separator: string + ssr: boolean + dirty: boolean + domElementCount: number + tagTypeCounts: Record + validationRules: SerializedValidationRule[] +} diff --git a/packages/devtools/src/vite.ts b/packages/devtools/src/vite.ts new file mode 100644 index 000000000..8a54ab3e0 --- /dev/null +++ b/packages/devtools/src/vite.ts @@ -0,0 +1,169 @@ +/// +import type { Plugin } from 'vite' +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import MagicString from 'magic-string' +import { parseAndWalk } from 'oxc-walker' +import { getConfigRpc } from './rpc' + +const HEAD_COMPOSABLES = ['useHead', 'useSeoMeta', 'useHeadSafe', 'useScript'] +const HEAD_FACTORIES = ['createHead'] +const FILE_RE = /\.(vue|tsx?|jsx?|svelte)$/ + +const UNHEAD_ICON = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%2300dc82' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39'/%3E%3C/svg%3E` + +const DEVTOOLS_UI_ROUTE = '/__unhead/' + +function hasHeadCode(code: string): boolean { + return HEAD_COMPOSABLES.some(c => code.includes(c)) || HEAD_FACTORIES.some(c => code.includes(c)) +} + +/** + * Transforms source code to inject `_source` metadata into head composable calls. + */ +function transformSourceLocations(code: string, id: string, root: string): { code: string, map: any } | undefined { + if (!hasHeadCode(code)) + return + + const s = new MagicString(code) + let transformed = false + + const relativePath = id.startsWith(root) + ? id.slice(root.length).replace(/^\//, '') + : id + + parseAndWalk(code, id, { + parseOptions: { lang: 'ts' }, + enter(node: any) { + if (node.type !== 'CallExpression') + return + const callee = node.callee + if (!callee) + return + + const name = callee.type === 'Identifier' + ? callee.name + : callee.type === 'MemberExpression' && callee.property?.type === 'Identifier' + ? callee.property.name + : null + + // Detect createHead() and wrap to expose on window for the bridge + if (name && HEAD_FACTORIES.includes(name)) { + s.prependLeft(node.start, `((_h)=>(typeof window!=='undefined'&&(window.__unhead_devtools__=_h),_h))(`) + s.appendRight(node.end, `)`) + transformed = true + return + } + + if (!name || !HEAD_COMPOSABLES.includes(name)) + return + + const args = node.arguments + if (!args || args.length === 0) + return + + const lineNumber = code.slice(0, node.start).split('\n').length + const sourceValue = `${relativePath}:${lineNumber}` + + if (args.length === 1) { + const argEnd = args[0].end + s.appendRight(argEnd, `, { _source: ${JSON.stringify(sourceValue)} }`) + transformed = true + } + else if (args.length >= 2 && args[1].type === 'ObjectExpression') { + const objStart = args[1].start + 1 + s.appendRight(objStart, ` _source: ${JSON.stringify(sourceValue)},`) + transformed = true + } + }, + }) + + if (!transformed) + return + + return { + code: s.toString(), + map: s.generateMap({ includeContent: true, source: id }), + } +} + +export function unheadDevtools(): Plugin { + let root = '' + let bridgeCode: string | undefined + const pkgDir = fileURLToPath(new URL('..', import.meta.url)) + + return { + name: '@unhead/devtools', + apply: 'serve', + + configResolved(config) { + root = config.root + const bridgePath = resolve(pkgDir, 'dist/bridge.mjs') + if (existsSync(bridgePath)) + bridgeCode = readFileSync(bridgePath, 'utf-8') + }, + + configureServer(server) { + // Bridge middleware + server.middlewares.use('/@unhead/bridge.mjs', async (_req, res) => { + const result = await server.transformRequest('/@unhead/bridge.mjs') + res.setHeader('Content-Type', 'application/javascript') + res.end(result?.code || 'console.warn("[unhead devtools] bridge not built")') + }) + }, + + resolveId(id) { + if (id === '/@unhead/bridge.mjs') + return id + }, + + load(id) { + if (id !== '/@unhead/bridge.mjs') + return + if (!bridgeCode) + return 'console.warn("[unhead devtools] bridge not built")' + const kitClientPath = resolve(pkgDir, 'node_modules/@vitejs/devtools-kit/dist/client.js') + if (existsSync(kitClientPath)) + return bridgeCode.replace(`'@vitejs/devtools-kit/client'`, `'${kitClientPath}'`) + return bridgeCode + }, + + transform: { + filter: { id: FILE_RE }, + handler(code, id) { + return transformSourceLocations(code, id, root) + }, + }, + + transformIndexHtml() { + return [{ + tag: 'script', + attrs: { type: 'module' }, + children: `import("/@unhead/bridge.mjs")`, + injectTo: 'body-close', + }] + }, + + devtools: { + setup(ctx) { + const clientPath = resolve(pkgDir, '../devtools-app/dist') + if (existsSync(clientPath)) { + ctx.views.hostStatic(DEVTOOLS_UI_ROUTE, clientPath) + } + + ctx.docks.register({ + id: 'unhead', + title: 'Unhead', + icon: UNHEAD_ICON, + type: 'iframe', + url: DEVTOOLS_UI_ROUTE, + }) + + ctx.rpc.register(getConfigRpc) + }, + }, + } +} + +export default unheadDevtools diff --git a/packages/devtools/tsconfig.json b/packages/devtools/tsconfig.json new file mode 100644 index 000000000..75eba9f99 --- /dev/null +++ b/packages/devtools/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/unhead/src/plugins/validate.ts b/packages/unhead/src/plugins/validate.ts index 3b36ab93d..2094cc52f 100644 --- a/packages/unhead/src/plugins/validate.ts +++ b/packages/unhead/src/plugins/validate.ts @@ -705,6 +705,9 @@ export function ValidatePlugin(options: ValidatePluginOptions = {}) { } } + // Store rules on the head instance for devtools integration + ;(head as any)._validationRules = rules + // Dispatch if (rules.length) { if (options.onReport) { diff --git a/packages/unhead/src/types/head.ts b/packages/unhead/src/types/head.ts index 833268df9..237ca73d0 100644 --- a/packages/unhead/src/types/head.ts +++ b/packages/unhead/src/types/head.ts @@ -200,6 +200,11 @@ export interface HeadEntryOptions extends TagPosition, TagPriority, ProcessesTem * @internal */ _index?: number + /** + * Source location for devtools tracing. + * @internal + */ + _source?: string } export type HeadRenderer = (head: Unhead) => T diff --git a/packages/unhead/src/types/tags.ts b/packages/unhead/src/types/tags.ts index 91f89e562..7f2d13440 100644 --- a/packages/unhead/src/types/tags.ts +++ b/packages/unhead/src/types/tags.ts @@ -115,6 +115,11 @@ export interface HeadTag extends TagPriority, TagPosition, ResolvesDuplicates, H * @internal */ _h?: string + /** + * Source file:line that created this tag (devtools only). + * @internal + */ + _source?: string } export type HeadTagKeys = (keyof HeadTag)[] diff --git a/packages/vue/src/composables.ts b/packages/vue/src/composables.ts index bbb01bfaf..f9547d829 100644 --- a/packages/vue/src/composables.ts +++ b/packages/vue/src/composables.ts @@ -96,3 +96,10 @@ export function useSeoMeta(input: UseSeoMetaInput = {}, options: UseHeadOptions } export { useScript } from './scripts/useScript' + +/** @deprecated Use `useHead` instead. */ +export const useServerHead = useHead +/** @deprecated Use `useHeadSafe` instead. */ +export const useServerHeadSafe = useHeadSafe +/** @deprecated Use `useSeoMeta` instead. */ +export const useServerSeoMeta = useSeoMeta diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 9c1781ed9..940701f89 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -1,7 +1,8 @@ export { unheadVueComposablesImports } from './autoImports' -export { injectHead, useHead, useHeadSafe, useScript, useSeoMeta } from './composables' +export { injectHead, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta } from './composables' export { headSymbol } from './install' export type * from './scripts/index' export type * from './types' +export { resolveUnrefHeadInput } from './utils' export { VueHeadMixin } from './VueHeadMixin' export { createUnhead } from 'unhead' diff --git a/packages/vue/src/utils.ts b/packages/vue/src/utils.ts index d29470c9b..9f6bdbede 100644 --- a/packages/vue/src/utils.ts +++ b/packages/vue/src/utils.ts @@ -1,2 +1,13 @@ -export { VueResolver } from './resolver' +import type { ResolvedHead } from 'unhead/types' +import { walkResolver } from 'unhead/utils' +import { VueResolver } from './resolver' + +export { VueResolver } export * from 'unhead/utils' + +/** + * @deprecated Use head.resolveTags() instead. + */ +export function resolveUnrefHeadInput(input: any): ResolvedHead { + return walkResolver(input, VueResolver) +} From 2dc40b1624db3a46ef103dd85b987f0426baa66a Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 8 Apr 2026 14:22:03 +1000 Subject: [PATCH 3/7] doc: clean up --- .../guides/get-started/0.installation.md | 2 +- .../guides/get-started/0.installation.md | 2 +- .../guides/get-started/0.installation.md | 2 +- .../guides/get-started/0.installation.md | 2 +- .../guides/0.get-started/1.installation.md | 2 +- .../guides/get-started/0.installation.md | 2 +- .../guides/0.get-started/0.installation.md | 2 +- .../head/1.guides/0.get-started/0.overview.md | 8 +- .../head/1.guides/1.core-concepts/1.titles.md | 11 +- .../1.guides/1.core-concepts/2.positions.md | 2 +- .../1.guides/1.core-concepts/3.class-attr.md | 4 +- .../2.advanced/11.extending-unhead.md | 216 ------------------ .../1.guides/2.advanced/7.client-only-tags.md | 123 ---------- .../head/1.guides/2.advanced/9.vite-plugin.md | 129 ----------- .../head/1.guides/build-plugins/0.overview.md | 108 +++++++++ .../1.guides/build-plugins/1.tree-shaking.md | 81 +++++++ .../build-plugins/2.seo-meta-transform.md | 104 +++++++++ .../build-plugins/3.minify-transform.md | 110 +++++++++ .../1.guides/plugins/6.template-params.md | 4 +- docs/head/1.guides/plugins/alias-sorting.md | 4 +- docs/head/1.guides/plugins/canonical.md | 47 +--- .../1.guides/plugins/infer-seo-meta-tags.md | 4 +- docs/head/1.guides/plugins/minify.md | 8 +- docs/head/1.guides/plugins/validate.md | 2 +- docs/head/7.api/0.get-started/overview.md | 4 + docs/head/7.api/composables/0.use-head.md | 127 ++++++++-- docs/head/7.api/composables/3.use-seo-meta.md | 51 ++++- docs/head/7.api/plugins.md | 88 +++++++ 28 files changed, 688 insertions(+), 561 deletions(-) delete mode 100644 docs/head/1.guides/2.advanced/11.extending-unhead.md delete mode 100644 docs/head/1.guides/2.advanced/7.client-only-tags.md delete mode 100644 docs/head/1.guides/2.advanced/9.vite-plugin.md create mode 100644 docs/head/1.guides/build-plugins/0.overview.md create mode 100644 docs/head/1.guides/build-plugins/1.tree-shaking.md create mode 100644 docs/head/1.guides/build-plugins/2.seo-meta-transform.md create mode 100644 docs/head/1.guides/build-plugins/3.minify-transform.md create mode 100644 docs/head/7.api/plugins.md diff --git a/docs/0.angular/schema-org/guides/get-started/0.installation.md b/docs/0.angular/schema-org/guides/get-started/0.installation.md index 7727841e5..098ccae28 100644 --- a/docs/0.angular/schema-org/guides/get-started/0.installation.md +++ b/docs/0.angular/schema-org/guides/get-started/0.installation.md @@ -69,7 +69,7 @@ useSchemaOrg([ ## Recommended: Vite Plugin -If you're using Vite, the [Vite plugin](/docs/head/guides/advanced/vite-plugin) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time. See the [Vite Plugin guide](/docs/head/guides/advanced/vite-plugin) for framework-specific setup. +If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time. See the [Build Plugins guide](/docs/head/guides/build-plugins/overview) for framework-specific setup. ## Optional: Auto-Imports diff --git a/docs/0.react/schema-org/guides/get-started/0.installation.md b/docs/0.react/schema-org/guides/get-started/0.installation.md index 632b1cb79..4eb3b0067 100644 --- a/docs/0.react/schema-org/guides/get-started/0.installation.md +++ b/docs/0.react/schema-org/guides/get-started/0.installation.md @@ -67,7 +67,7 @@ useSchemaOrg([ ## Recommended: Vite Plugin -If you're using Vite, the [Vite plugin](/docs/head/guides/advanced/vite-plugin) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: +If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: ```ts import react from '@vitejs/plugin-react' diff --git a/docs/0.solid-js/schema-org/guides/get-started/0.installation.md b/docs/0.solid-js/schema-org/guides/get-started/0.installation.md index b1a128048..a08c9bc8e 100644 --- a/docs/0.solid-js/schema-org/guides/get-started/0.installation.md +++ b/docs/0.solid-js/schema-org/guides/get-started/0.installation.md @@ -117,7 +117,7 @@ See the [Schema.org Params](/docs/schema-org/guides/core-concepts/params) for al ## Recommended: Vite Plugin -If you're using Vite, the [Vite plugin](/docs/head/guides/advanced/vite-plugin) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: +If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: ```ts [vite.config.ts] import solid from 'vite-plugin-solid' diff --git a/docs/0.svelte/schema-org/guides/get-started/0.installation.md b/docs/0.svelte/schema-org/guides/get-started/0.installation.md index 1f8caed03..ca892b30b 100644 --- a/docs/0.svelte/schema-org/guides/get-started/0.installation.md +++ b/docs/0.svelte/schema-org/guides/get-started/0.installation.md @@ -67,7 +67,7 @@ useSchemaOrg([ ## Recommended: Vite Plugin -If you're using Vite, the [Vite plugin](/docs/head/guides/advanced/vite-plugin) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: +If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: ```ts import { svelte } from '@sveltejs/vite-plugin-svelte' diff --git a/docs/0.typescript/head/guides/0.get-started/1.installation.md b/docs/0.typescript/head/guides/0.get-started/1.installation.md index 23f035366..35994d69d 100644 --- a/docs/0.typescript/head/guides/0.get-started/1.installation.md +++ b/docs/0.typescript/head/guides/0.get-started/1.installation.md @@ -160,4 +160,4 @@ Your app is now setup for head management, congrats! Try next: 1. Learn more about app context in the [Wrapping Composables](/docs/typescript/head/guides/core-concepts/wrapping-composables) guide -2. Consider using the [Vite plugin](/docs/head/guides/advanced/vite-plugin) +2. Consider using the [Vite plugin](/docs/head/guides/build-plugins/overview) diff --git a/docs/0.typescript/schema-org/guides/get-started/0.installation.md b/docs/0.typescript/schema-org/guides/get-started/0.installation.md index 9fd9206b1..2f7363acb 100644 --- a/docs/0.typescript/schema-org/guides/get-started/0.installation.md +++ b/docs/0.typescript/schema-org/guides/get-started/0.installation.md @@ -67,7 +67,7 @@ useSchemaOrg([ ## Recommended: Vite Plugin -If you're using Vite, the [Vite plugin](/docs/head/guides/advanced/vite-plugin) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time. See the [Vite Plugin guide](/docs/head/guides/advanced/vite-plugin) for framework-specific setup. +If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time. See the [Build Plugins guide](/docs/head/guides/build-plugins/overview) for framework-specific setup. ## Optional: Auto-Imports diff --git a/docs/0.vue/schema-org/guides/0.get-started/0.installation.md b/docs/0.vue/schema-org/guides/0.get-started/0.installation.md index b889cf52d..c6c2dd51c 100644 --- a/docs/0.vue/schema-org/guides/0.get-started/0.installation.md +++ b/docs/0.vue/schema-org/guides/0.get-started/0.installation.md @@ -71,7 +71,7 @@ useSchemaOrg([ ## Recommended: Vite Plugin -If you're using Vite, the [Vite plugin](/docs/head/guides/advanced/vite-plugin) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: +If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview) optimizes your production bundle by tree-shaking server-only code and transforming head composables at build time: ```ts import vue from '@vitejs/plugin-vue' diff --git a/docs/head/1.guides/0.get-started/0.overview.md b/docs/head/1.guides/0.get-started/0.overview.md index 4586955dc..2696652bb 100644 --- a/docs/head/1.guides/0.get-started/0.overview.md +++ b/docs/head/1.guides/0.get-started/0.overview.md @@ -51,13 +51,11 @@ Master the fundamental concepts behind Unhead's powerful head management. Take your head management to the next level with these advanced capabilities. -::div{class="grid grid-cols-3 gap-5"} - -:UPageCard{title="Extending Unhead" description="Create custom plugins and extend core functionality" to="/docs/head/guides/advanced/extending-unhead" icon="i-heroicons-cog-8-tooth" spotlight spotlight-color="warning"} +::div{class="grid grid-cols-2 gap-5"} -:UPageCard{title="Bundle Optimizations" description="Optimize your bundle size when using Unhead" to="/docs/head/guides/advanced/client-only-tags" icon="i-heroicons-computer-desktop" spotlight spotlight-color="warning"} +:UPageCard{title="Plugins API" description="Create custom plugins and extend core functionality" to="/docs/head/api/plugins" icon="i-heroicons-cog-8-tooth" spotlight spotlight-color="warning"} -:UPageCard{title="Build Plugins" description="Leverage build tools to optimize Unhead usage" to="/docs/head/guides/advanced/vite-plugin" icon="i-heroicons-puzzle-piece" spotlight spotlight-color="warning"} +:UPageCard{title="Build Plugins" description="Leverage build tools to optimize Unhead usage" to="/docs/head/guides/build-plugins/overview" icon="i-heroicons-puzzle-piece" spotlight spotlight-color="warning"} :: diff --git a/docs/head/1.guides/1.core-concepts/1.titles.md b/docs/head/1.guides/1.core-concepts/1.titles.md index 1222bea2f..b5a734c6f 100644 --- a/docs/head/1.guides/1.core-concepts/1.titles.md +++ b/docs/head/1.guides/1.core-concepts/1.titles.md @@ -240,9 +240,10 @@ Here are some practical examples for handling page titles in different scenarios Titles can be reactive, updating when your component data changes. Here's how this works in different frameworks: -::code-group +::FrameworkCode -```ts [Vue] +#vue +```ts import { useHead } from '@unhead/dynamic-import' import { ref } from 'vue' @@ -256,7 +257,8 @@ useHead({ }) ``` -```tsx [React] +#react +```tsx import { useHead } from '@unhead/dynamic-import' import { useState } from 'react' @@ -274,7 +276,8 @@ function ProductPage() { } ``` -```tsx [Solid] +#solid +```tsx import { useHead } from '@unhead/dynamic-import' import { createSignal } from 'solid-js' diff --git a/docs/head/1.guides/1.core-concepts/2.positions.md b/docs/head/1.guides/1.core-concepts/2.positions.md index 1806bf791..b885b1060 100644 --- a/docs/head/1.guides/1.core-concepts/2.positions.md +++ b/docs/head/1.guides/1.core-concepts/2.positions.md @@ -142,7 +142,7 @@ If you need full control over tag ordering, you can provide a custom `tagWeight` The default `capoTagWeight` function is exported from `unhead/server` so you can wrap it: ```ts -import { createHead, capoTagWeight } from 'unhead/server' +import { createHead, capoTagWeight } from '@unhead/dynamic-import/server' const head = createHead({ tagWeight(tag) { diff --git a/docs/head/1.guides/1.core-concepts/3.class-attr.md b/docs/head/1.guides/1.core-concepts/3.class-attr.md index 92d704f0d..1f9042956 100644 --- a/docs/head/1.guides/1.core-concepts/3.class-attr.md +++ b/docs/head/1.guides/1.core-concepts/3.class-attr.md @@ -42,7 +42,7 @@ useHead({ :: ::tip -If you're server-side rendering static tags, you can make use of [Client-Only Tags](/docs/head/guides/advanced/client-only-tags). +If you're server-side rendering static tags, you can use `import.meta.server` to conditionally add them only during SSR. :: ### Can I Use Arrays for Classes and Styles? @@ -125,4 +125,4 @@ useHead({ ## See Also - [useHead() API](/docs/head/api/composables/use-head) - Full API reference -- [Bundle Optimizations](/docs/head/guides/advanced/client-only-tags) - Client-only attributes +- [Build Plugins](/docs/head/guides/build-plugins/overview) - Build-time optimizations diff --git a/docs/head/1.guides/2.advanced/11.extending-unhead.md b/docs/head/1.guides/2.advanced/11.extending-unhead.md deleted file mode 100644 index c3e548546..000000000 --- a/docs/head/1.guides/2.advanced/11.extending-unhead.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -title: Extending Unhead -description: Create custom composables and plugins with Unhead's hooks API. Tap into tag resolution, DOM rendering, and SSR lifecycle events. -navigation.title: Extending Unhead ---- - -## Introduction - -Unhead is designed with extensibility in mind, providing lower-level primitives that can be composed to create powerful functionality. This guide explores how to extend Unhead using hooks and plugins to meet your specific requirements. - -## Understanding the Architecture - -Unhead uses a hooks-based architecture powered by [unjs/hookable](https://github.com/unjs/hookable), allowing you to tap into different parts of the head tag management lifecycle. This enables you to create custom features without modifying the core library. - -### Hook Execution Sequence - -Understanding the order in which hooks are executed is important for creating plugins that work well together. Here is the typical flow: - -1. **Entry Processing**: - -- `entries:updated` -- `entries:resolve` -- For each entry: `entries:normalize` -1. **Tag Processing**: - -- For each tag: `tag:normalise` -- `tags:beforeResolve` -- `tags:resolve` -- `tags:afterResolve` -1. **Client-side Rendering**: - -- `dom:beforeRender` -1. **Server-side Rendering**: - -- `ssr:beforeRender` -- `ssr:render` -- `ssr:rendered` -1. **Script Management**: - -- When applicable: `script:updated` - -### Available Hooks - -Unhead provides several hooks you can use to extend functionality: - -```ts -import { createHead, useHead } from '@unhead/dynamic-import' - -const head = createHead({ - hooks: { - 'entries:resolve': (ctx) => { - // Called when entries need to be resolved to tags - }, - 'tags:resolve': (ctx) => { - // Called when tags are being resolved for rendering - }, - 'tag:normalise': (ctx) => { - // Called when a tag is being normalized - }, - 'tag:generated': (ctx) => { - // Called after a tag has been generated - } - // See full list in the API reference - } -}) -``` - -## Accessing Head State - -The recommended way to access the head state is through the `resolveTags` function: - -```ts -import { injectHead, useHead } from '@unhead/dynamic-import' - -const head = injectHead() -const tags = head.resolveTags() - -// Now you can inspect or manipulate the tags -console.log(tags) -``` - -This gives you access to the fully processed tags that would be rendered to the DOM. - -## Creating Custom Composables - -Unhead's composables like `useHead()` and `useSeoMeta()` are built on top of primitive APIs. You can create your own composables for specific use cases. - -### Example: Creating useTitle Composable - -```ts -import { useHead } from '@unhead/dynamic-import' - -export function useTitle(title: string, options = {}) { - return useHead({ - title, - }, options) -} -``` - -### Example: Creating useBodyClass Composable - -```ts -import { useHead } from '@unhead/dynamic-import' - -export function useBodyClass(classes: string | string[]) { - const classList = Array.isArray(classes) ? classes : [classes] - - return useHead({ - bodyAttrs: { - class: classList.join(' ') - } - }) -} -``` - -## Building Plugins - -For more complex extensions, you can create plugins that hook into multiple parts of Unhead's lifecycle. - -### Example: Custom Deduplication Plugin - -```ts -import { defineHeadPlugin } from '@unhead/dynamic-import' - -export const customDedupePlugin = defineHeadPlugin({ - hooks: { - 'tags:resolve': (ctx) => { - // Custom logic to deduplicate tags - ctx.tags = deduplicateTagsWithCustomLogic(ctx.tags) - } - } -}) - -// Usage -const head = createHead({ - plugins: [ - customDedupePlugin() - ] -}) -``` - -## Common Use Cases - -### Example: Tailwind Class Deduplication - -This example shows how to deduplicate Tailwind CSS classes using `tailwind-merge`: - -```ts -import { defineHeadPlugin } from '@unhead/dynamic-import' -import { twMerge } from 'tailwind-merge' - -export const tailwindMergePlugin = defineHeadPlugin({ - hooks: { - 'tags:resolve': (ctx) => { - // Find body tags with class attributes - ctx.tags.forEach((tag) => { - if (tag.tag === 'bodyAttrs' && tag.props.class) { - // Deduplicate classes with tailwind-merge - tag.props.class = twMerge(tag.props.class) - } - }) - } - } -}) -``` - -### Example: Custom MetaInfo Provider - -Create a plugin that pulls meta information from a global store: - -```ts -import { defineHeadPlugin } from '@unhead/dynamic-import' - -export const storeMetaPlugin = defineHeadPlugin({ - hooks: { - 'entries:resolve': (ctx) => { - // Add entries from a store - const storeMetaInfo = getMetaFromStore() - ctx.entries.push(storeMetaInfo) - } - } -}) -``` - -## Best Practices - -::tip -When extending Unhead: - -- Keep extensions focused on a single concern -- Use typed hooks for better developer experience -- Document your extensions for team usage -- Consider performance implications in your hooks -- Test extensions with a variety of input cases -:: - -## API Reference - -For a complete list of available hooks and their signatures, refer to the hooks definitions in the source code: - -```ts -// From packages/unhead/src/types/hooks.ts -export interface HeadHooks { - 'entries:updated': (ctx: Unhead) => HookResult - 'entries:resolve': (ctx: EntryResolveCtx) => SyncHookResult - 'entries:normalize': (ctx: { tags: HeadTag[], entry: HeadEntry }) => SyncHookResult - 'tag:normalise': (ctx: { tag: HeadTag, entry: HeadEntry, resolvedOptions: CreateClientHeadOptions }) => SyncHookResult - 'tags:beforeResolve': (ctx: TagResolveContext) => SyncHookResult - 'tags:resolve': (ctx: TagResolveContext) => SyncHookResult - 'tags:afterResolve': (ctx: TagResolveContext) => SyncHookResult - 'dom:beforeRender': (ctx: DomBeforeRenderCtx) => SyncHookResult - // ...additional hooks -} -``` - -Note: `SyncHookResult` is `void` (synchronous only), while `HookResult` is `void | Promise`. diff --git a/docs/head/1.guides/2.advanced/7.client-only-tags.md b/docs/head/1.guides/2.advanced/7.client-only-tags.md deleted file mode 100644 index f3512e08c..000000000 --- a/docs/head/1.guides/2.advanced/7.client-only-tags.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: Bundle Optimizations -description: Reduce bundle size with client-only and server-only tags. Use mode option or import.meta guards for SSR/CSR optimization. -navigation.title: Bundle Optimizations ---- - -**Quick Answer:** To reduce bundle size, use `import.meta.client` to conditionally add tags only on the client. For Vite, use the Unhead Vite plugin to tree-shake server-only code. - -Unhead runs on both server and client. You can conditionally render tags in one environment to optimize bundle size. - -## How Do I Add Client-Only Tags? - -Client-only tags render exclusively in the browser, reducing server-side bundle size. - -### When to Use Client-Only Tags - -- Analytics scripts (Google Analytics, Plausible, etc.) -- User tracking and personalization scripts -- Client-side feature detection -- Progressive enhancement - -Use `import.meta.client` to conditionally add tags: - -```ts -import { useHead } from '@unhead/dynamic-import' - -// Using import.meta environment variables -if (import.meta.client) { - useHead({ - script: [ - { - src: 'https://example.com/analytics.js', - defer: true - } - ] - }) -} -``` - -## How Do I Add Server-Only Tags? - -Server-only tags render during SSR, reducing client-side bundle size. - -### When to Use Server-Only Tags - -- SEO metadata that doesn't need client reactivity -- Open Graph images and social media metadata -- Static metadata that appears on every page -- Schema.org structured data - -Use `import.meta.server` to conditionally add tags: - -```ts -import { useHead } from '@unhead/dynamic-import' - -if (import.meta.server) { - useHead({ - meta: [ - { - property: 'og:image', - content: 'https://example.com/my-image.jpg' - } - ] - }) -} -``` - -## Example Use Cases - -### Analytics After Hydration - -Load analytics only after the application has hydrated: - -```ts -import { useHead } from '@unhead/dynamic-import' - -if (import.meta.client) { - useHead({ - script: [ - { - src: 'https://example.com/analytics.js', - defer: true, - async: true - } - ] - }) -} -``` - -### Static SEO Tags on Server - -Add SEO metadata that doesn't need client updates: - -```ts -import { useHead } from '@unhead/dynamic-import' - -if (import.meta.server) { - useHead({ - meta: [ - { name: 'robots', content: 'index, follow' }, - { name: 'description', content: 'Site description' } - ] - }) -} -``` - -## What Are the Caveats? - -::warning -Some tags have dependencies that span client and server rendering: - -- `titleTemplate` affects `title` rendering - include on both client and server to avoid title flashing -- Tags with `tagPosition` or `tagPriority`{lang="ts"} may behave differently if not consistently applied -- Event handlers are only triggered in their respective environments -:: - -Ensure dependent tags are included in both environments when needed. - -## See Also - -- [Tag Positions](/docs/head/guides/core-concepts/positions) - Control tag placement -- [Vite Plugin](/docs/head/guides/advanced/vite-plugin) - Build-time optimizations -- [useHead() API](/docs/head/api/composables/use-head) - Full API reference diff --git a/docs/head/1.guides/2.advanced/9.vite-plugin.md b/docs/head/1.guides/2.advanced/9.vite-plugin.md deleted file mode 100644 index 82ad46e95..000000000 --- a/docs/head/1.guides/2.advanced/9.vite-plugin.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: Vite Plugin -description: Unified Vite plugin for Unhead. Build optimizations, streaming SSR, tree-shaking, and useSeoMeta transforms in a single plugin call. -navigation.title: Vite Plugin ---- - -**Quick Answer:** Each framework package ships a unified Vite plugin at `@unhead/{framework}/vite` that handles build optimizations and optional streaming SSR. One import, one plugin call. - -## Setup - -Install your framework's unhead package (if you haven't already), then add the plugin to your Vite config: - -::code-group - -```ts [Vue] -import vue from '@vitejs/plugin-vue' -import unhead from '@unhead/vue/vite' - -export default defineConfig({ - plugins: [vue(), unhead()], -}) -``` - -```ts [React] -import react from '@vitejs/plugin-react' -import unhead from '@unhead/react/vite' - -export default defineConfig({ - plugins: [react(), unhead()], -}) -``` - -```ts [Svelte] -import { svelte } from '@sveltejs/vite-plugin-svelte' -import unhead from '@unhead/svelte/vite' - -export default defineConfig({ - plugins: [unhead(), svelte()], -}) -``` - -```ts [Solid] -import solid from 'vite-plugin-solid' -import unhead from '@unhead/solid-js/vite' - -export default defineConfig({ - // Must come before solid() to see JSX before compilation - plugins: [unhead(), solid()], -}) -``` - -```ts [Nuxt (automatic)] -// No configuration needed -``` - -:: - -## What Does It Do? - -The plugin performs several build-time optimizations automatically: - -- **Tree-shaking**: Removes server-only composables (`useServerHead`, `useServerSeoMeta`, `useSchemaOrg`) from client bundles -- **`useSeoMeta` transform**: Converts `useSeoMeta()` calls into raw `useHead()` calls at build time (~3kb savings) -- **Streaming SSR** (opt-in): Auto-injects streaming head components for SSR streaming support - -## Options - -```ts -unhead({ - // Disable server composable tree-shaking - treeshake: false, - // Disable useSeoMeta → useHead transform - transformSeoMeta: false, - // Enable streaming SSR - streaming: true, - // Streaming with custom mode - streaming: { mode: 'inline' }, - // Pre-minify inline script/style content - minify: { js: createJSMinifier(), css: createCSSMinifier() }, - // File filter (shared across all transforms) - filter: { exclude: [/some-file/] }, -}) -``` - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `treeshake` | `object \| false` | enabled | Tree-shake server composables from client bundles | -| `transformSeoMeta` | `object \| false` | enabled | Transform `useSeoMeta()` to `useHead()` | -| `minify` | `object \| false` | disabled | Pre-minify static inline script/style. See [Minify Plugin](/docs/head/guides/plugins/minify#build-time-minification) | -| `streaming` | `true \| object \| false` | `false` | Enable streaming SSR support | -| `filter` | `object` | — | Shared include/exclude file filter | - -Every option accepts `false` to disable it entirely, or an object to configure it. - -## Streaming SSR - -When enabled, the streaming plugin auto-injects `` components into your templates and sets up the hydration client: - -```ts -unhead({ streaming: true }) - -// Or configure the streaming mode -unhead({ - streaming: { - // 'async' (default) - non-blocking async script - // 'inline' - inline IIFE in HTML head (larger HTML, immediate execution) - // 'module' - ES module import (waits for bundle) - mode: 'async', - }, -}) -``` - -## Webpack - -For Webpack projects, use `@unhead/bundler/webpack` directly: - -```ts -import { UnheadWebpack } from '@unhead/bundler/webpack' - -export default { - plugins: [UnheadWebpack()], -} -``` - -## See Also - -- [Minify Plugin](/docs/head/guides/plugins/minify) - Inline script/style minification -- [Client-Only Tags](/docs/head/guides/advanced/client-only-tags) - Bundle optimizations -- [useHead() API](/docs/head/api/composables/use-head) - Full API reference diff --git a/docs/head/1.guides/build-plugins/0.overview.md b/docs/head/1.guides/build-plugins/0.overview.md new file mode 100644 index 000000000..a7e9fc2c9 --- /dev/null +++ b/docs/head/1.guides/build-plugins/0.overview.md @@ -0,0 +1,108 @@ +--- +title: Build Plugins +description: Unified Vite and Webpack plugins for Unhead. Build optimizations, tree-shaking, and useSeoMeta transforms in a single plugin call. +navigation.title: Overview +--- + +**Quick Answer:** Each framework package ships a unified Vite plugin at `@unhead/{framework}/vite` that handles build optimizations. One import, one plugin call. + +## Setup + +Install your framework's unhead package (if you haven't already), then add the plugin to your Vite config: + +::FrameworkCode + +#vue +```ts [vite.config.ts] +import vue from '@vitejs/plugin-vue' +import unhead from '@unhead/vue/vite' + +export default defineConfig({ + plugins: [vue(), unhead()], +}) +``` + +#react +```ts [vite.config.ts] +import react from '@vitejs/plugin-react' +import unhead from '@unhead/react/vite' + +export default defineConfig({ + plugins: [react(), unhead()], +}) +``` + +#svelte +```ts [vite.config.ts] +import { svelte } from '@sveltejs/vite-plugin-svelte' +import unhead from '@unhead/svelte/vite' + +export default defineConfig({ + plugins: [unhead(), svelte()], +}) +``` + +#solid +```ts [vite.config.ts] +import solid from 'vite-plugin-solid' +import unhead from '@unhead/solid-js/vite' + +export default defineConfig({ + // Must come before solid() to see JSX before compilation + plugins: [unhead(), solid()], +}) +``` + +#nuxt +```ts +// No configuration needed +``` + +:: + +## What Does It Do? + +The plugin combines several build-time optimizations: + +- **[Tree-shaking](/docs/head/guides/build-plugins/tree-shaking)**: Removes deprecated server composables and `useSchemaOrg` calls from client bundles +- **[useSeoMeta transform](/docs/head/guides/build-plugins/seo-meta-transform)**: Converts `useSeoMeta()` calls into raw `useHead()` calls at build time (~3kb savings) +- **[Minify transform](/docs/head/guides/build-plugins/minify-transform)**: Pre-minify static inline script/style content at build time +- **[Devtools](/docs/head/guides/build-plugins/devtools)**: Inspect head tags, entries, and SEO metadata in Vite DevTools (dev only, enabled by default) + +## Options + +```ts +unhead({ + // Disable server composable tree-shaking + treeshake: false, + // Disable useSeoMeta → useHead transform + transformSeoMeta: false, + // Pre-minify inline script/style content + minify: { js: createJSMinifier(), css: createCSSMinifier() }, + // File filter (shared across all transforms) + filter: { exclude: [/some-file/] }, +}) +``` + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `treeshake` | `object \| false` | enabled | [Tree-shake server composables](/docs/head/guides/build-plugins/tree-shaking) from client bundles | +| `transformSeoMeta` | `object \| false` | enabled | [Transform `useSeoMeta()`](/docs/head/guides/build-plugins/seo-meta-transform) to `useHead()` | +| `minify` | `object \| false` | disabled | [Pre-minify static inline script/style](/docs/head/guides/build-plugins/minify-transform) | +| `devtools` | `object \| false` | enabled | [Vite DevTools integration](/docs/head/guides/build-plugins/devtools) (dev only) | +| `filter` | `object` | — | Shared include/exclude file filter | +| `sourcemap` | `boolean` | — | Enable sourcemap generation for transforms | + +Every option accepts `false` to disable it entirely, or an object to configure it. + +## Webpack + +For Webpack projects, use `@unhead/bundler/webpack` directly: + +```ts +import UnheadWebpack from '@unhead/bundler/webpack' + +export default { + plugins: [UnheadWebpack()], +} +``` diff --git a/docs/head/1.guides/build-plugins/1.tree-shaking.md b/docs/head/1.guides/build-plugins/1.tree-shaking.md new file mode 100644 index 000000000..acdd0b6e6 --- /dev/null +++ b/docs/head/1.guides/build-plugins/1.tree-shaking.md @@ -0,0 +1,81 @@ +--- +title: Tree-Shaking +description: Automatically remove server-only composables from client bundles at build time. +navigation.title: Tree-Shaking +--- + +**Quick Answer:** The tree-shaking transform strips deprecated server composables and `useSchemaOrg` calls from client bundles so they never ship to the browser. Enabled by default. + +## What Does It Do? + +This transform removes calls to composables that should only run on the server. On client builds these calls are dead code; removing them reduces bundle size. + +Composables removed from client bundles: +- `useServerHead()` (deprecated, use `useHead()`) +- `useServerHeadSafe()` (deprecated, use `useHeadSafe()`) +- `useServerSeoMeta()` (deprecated, use `useSeoMeta()`) +- `useSchemaOrg()` + +::code-group + +```ts [Before (source)] +useHead({ title: 'My Page' }) + +useSchemaOrg([ + defineWebPage({ name: 'My Page' }), +]) +``` + +```ts [After (client build)] +useHead({ title: 'My Page' }) +``` + +:: + +::callout{type="info"} +`useServerHead`, `useServerHeadSafe`, and `useServerSeoMeta` are deprecated aliases. They exist for backwards compatibility and are identical to their non-server counterparts. New code should use `useHead`, `useHeadSafe`, and `useSeoMeta` directly. The tree-shaking transform still removes calls to the deprecated names for codebases that haven't migrated yet. +:: + +The transform only runs on client builds. SSR builds are left untouched. + +## Setup + +Tree-shaking is enabled by default when using the [unified Vite plugin](/docs/head/guides/build-plugins/overview): + +```ts +import unhead from '@unhead/vue/vite' + +export default defineConfig({ + plugins: [unhead()], +}) +``` + +### Disable + +```ts +unhead({ + treeshake: false, +}) +``` + +## Options + +```ts +unhead({ + treeshake: { + // Override the shared file filter for this transform only + filter: { + include: [/my-special-file/], + exclude: [/some-file/], + }, + }, +}) +``` + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `filter` | `object` | inherited | Include/exclude file patterns (overrides the shared `filter`) | + +## How It Works + +The transform parses each file with [oxc-parser](https://github.com/nicolo-ribaudo/oxc-parser/tree/nicolo/oxc-parser) and walks the AST looking for `ExpressionStatement` nodes that call one of the server composable names. Matching statements are removed from the source using [magic-string](https://github.com/Rich-Harris/magic-string), preserving sourcemaps. diff --git a/docs/head/1.guides/build-plugins/2.seo-meta-transform.md b/docs/head/1.guides/build-plugins/2.seo-meta-transform.md new file mode 100644 index 000000000..3774f1960 --- /dev/null +++ b/docs/head/1.guides/build-plugins/2.seo-meta-transform.md @@ -0,0 +1,104 @@ +--- +title: useSeoMeta Transform +description: Convert useSeoMeta() calls to useHead() at build time, saving ~3kb from your client bundle. +navigation.title: useSeoMeta Transform +--- + +**Quick Answer:** The `useSeoMeta` transform rewrites `useSeoMeta()` calls into equivalent `useHead()` calls at build time. This removes the runtime meta-key resolution code (~3kb) from your bundle. Enabled by default. Also handles the deprecated `useServerSeoMeta()` for legacy code. + +## What Does It Do? + +At build time, `useSeoMeta()` calls with static or semi-static arguments are converted to `useHead()` with pre-resolved `meta` arrays: + +::code-group + +```ts [Before (source)] +useSeoMeta({ + title: 'My Title', + description: 'My Description', + ogImage: 'https://example.com/image.png', +}) +``` + +```ts [After (build output)] +useHead({ + title: 'My Title', + meta: [ + { name: 'description', content: 'My Description' }, + { property: 'og:image', content: 'https://example.com/image.png' }, + ], +}) +``` + +:: + +Import specifiers are also rewritten: `import { useSeoMeta } from 'unhead'` becomes `import { useHead } from 'unhead'`. + +## Setup + +The transform is enabled by default when using the [unified Vite plugin](/docs/head/guides/build-plugins/overview): + +```ts +import unhead from '@unhead/vue/vite' + +export default defineConfig({ + plugins: [unhead()], +}) +``` + +### Disable + +```ts +unhead({ + transformSeoMeta: false, +}) +``` + +## Options + +```ts +unhead({ + transformSeoMeta: { + // Disable import specifier rewriting + imports: false, + // Extra import paths to recognize as useSeoMeta sources + importPaths: ['my-custom-package'], + // Override the shared file filter + filter: { + include: [/my-special-file/], + exclude: [/some-file/], + }, + }, +}) +``` + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `imports` | `boolean` | `true` | Rewrite import specifiers (`useSeoMeta` → `useHead`) | +| `importPaths` | `string[]` | — | Additional package names to treat as valid `useSeoMeta` sources | +| `filter` | `object` | inherited | Include/exclude file patterns (overrides the shared `filter`) | + +## Limitations + +The transform skips calls it cannot statically analyze: +- Spread elements in the options object (`useSeoMeta({ ...dynamicMeta })`) +- Non-identifier keys +- Properties with missing values +- Object-valued properties with non-static (non-string-literal) sub-properties + +In these cases, the original `useSeoMeta()` call is preserved and resolved at runtime. + +### Deprecated `useServerSeoMeta` + +The transform also handles the deprecated `useServerSeoMeta()`. When it includes `title` or `titleTemplate`, the transform splits the call into two: a `useHead()` call for the title fields and a `useServerHead()` call for the meta tags. + +```ts +// Input (deprecated) +useServerSeoMeta({ title: 'My Title', description: 'My Desc' }) + +// Output +useHead({ title: 'My Title' }); +useServerHead({ meta: [{ name: 'description', content: 'My Desc' }] }) +``` + +New code should use `useSeoMeta()` instead. diff --git a/docs/head/1.guides/build-plugins/3.minify-transform.md b/docs/head/1.guides/build-plugins/3.minify-transform.md new file mode 100644 index 000000000..992eb4265 --- /dev/null +++ b/docs/head/1.guides/build-plugins/3.minify-transform.md @@ -0,0 +1,110 @@ +--- +title: Minify Transform +description: Pre-minify static inline script and style content inside useHead() calls at build time using rolldown, esbuild, or lightningcss. +navigation.title: Minify Transform +--- + +**Quick Answer:** The minify transform runs at build time and minifies static string literals inside `useHead()` script and style tags. Uses heavier tools (rolldown/esbuild for JS, lightningcss for CSS) that never enter your SSR runtime bundle. Disabled by default, requires a minifier to be provided. + +## What Does It Do? + +The transform finds `innerHTML` and `textContent` properties on `script` and `style` objects inside `useHead()` calls, then minifies them at build time: + +::code-group + +```ts [Before (source)] +useHead({ + script: [{ + innerHTML: ` + // Analytics initialization + window.dataLayer = window.dataLayer || []; + function gtag() { dataLayer.push(arguments); } + gtag('js', new Date()); + `, + }], +}) +``` + +```ts [After (build output)] +useHead({ + script: [{ + innerHTML: "window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag(\"js\",new Date)", + }], +}) +``` + +:: + +The transform automatically skips `application/json`, `application/ld+json`, `speculationrules`, and `importmap` script types, and ignores strings shorter than 20 characters. + +## Setup + +The minify transform is disabled by default. Enable it by providing minifier functions: + +```ts +import unhead from '@unhead/vue/vite' +import { createJSMinifier } from '@unhead/bundler/minify/rolldown' +import { createCSSMinifier } from '@unhead/bundler/minify/lightningcss' + +export default defineConfig({ + plugins: [ + unhead({ + minify: { + js: createJSMinifier(), + css: createCSSMinifier(), + }, + }), + ], +}) +``` + +## Available Minifier Backends + +| Package | Function | Use case | +|---------|----------|----------| +| `@unhead/bundler/minify/rolldown` | `createJSMinifier()` | JS minification (Vite 8+) | +| `@unhead/bundler/minify/esbuild` | `createJSMinifier()` | JS minification (Vite 7) | +| `@unhead/bundler/minify/lightningcss` | `createCSSMinifier()` | CSS minification | + +## Options + +```ts +unhead({ + minify: { + // Custom JS minifier, or false to disable + js: createJSMinifier(), + // Custom CSS minifier, or false to disable + css: createCSSMinifier(), + // Override the shared file filter + filter: { + include: [/my-special-file/], + exclude: [/some-file/], + }, + }, +}) +``` + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `js` | `false \| MinifyFn` | — | JS minifier function, or `false` to disable | +| `css` | `false \| MinifyFn` | — | CSS minifier function, or `false` to disable | +| `filter` | `object` | inherited | Include/exclude file patterns (overrides the shared `filter`) | + +### Custom Minifiers + +You can provide any async function that takes a string and returns the minified result (or `null` to skip): + +```ts +unhead({ + minify: { + js: async (code) => { + const result = await myMinifier(code) + return result.code + }, + }, +}) +``` + +## Runtime Minification + +For runtime SSR minification (not build-time), see the [Minify Plugin](/docs/head/guides/plugins/minify). The runtime plugin uses lightweight pure-JS minifiers suitable for edge and serverless environments. diff --git a/docs/head/1.guides/plugins/6.template-params.md b/docs/head/1.guides/plugins/6.template-params.md index 295f1a0c8..0e4c1ea05 100644 --- a/docs/head/1.guides/plugins/6.template-params.md +++ b/docs/head/1.guides/plugins/6.template-params.md @@ -17,8 +17,8 @@ Add the plugin to your Unhead configuration: ::code-block ```ts [Input] -import { createHead } from 'unhead' -import { TemplateParamsPlugin } from 'unhead/plugins' +import { createHead } from '@unhead/dynamic-import' +import { TemplateParamsPlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ diff --git a/docs/head/1.guides/plugins/alias-sorting.md b/docs/head/1.guides/plugins/alias-sorting.md index 912c9c499..afb7978ad 100644 --- a/docs/head/1.guides/plugins/alias-sorting.md +++ b/docs/head/1.guides/plugins/alias-sorting.md @@ -27,8 +27,8 @@ Add the plugin to your Unhead configuration: ::code-block ```ts [Input] -import { createHead } from 'unhead' -import { AliasSortingPlugin } from 'unhead/plugins' +import { createHead } from '@unhead/dynamic-import' +import { AliasSortingPlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ diff --git a/docs/head/1.guides/plugins/canonical.md b/docs/head/1.guides/plugins/canonical.md index f33384398..b1bdd63f3 100644 --- a/docs/head/1.guides/plugins/canonical.md +++ b/docs/head/1.guides/plugins/canonical.md @@ -54,7 +54,7 @@ Install the plugin in both your server & client entries: ::code-block ```ts [Input] -import { CanonicalPlugin } from 'unhead/plugins' +import { CanonicalPlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ @@ -237,15 +237,14 @@ CanonicalPlugin({ ## Framework Setup Guides -### Nuxt - -Nuxt has built-in Unhead support. Register the plugin in a [Nuxt plugin](https://nuxt.com/docs/guide/directory-structure/plugins): +Register the plugin when creating your head instance: -::code-block +::FrameworkCode +#nuxt ```ts [plugins/canonical.ts] import { injectHead } from '@unhead/vue' -import { CanonicalPlugin } from 'unhead/plugins' +import { CanonicalPlugin } from '@unhead/dynamic-import/plugins' export default defineNuxtPlugin(() => { const head = injectHead() @@ -255,14 +254,7 @@ export default defineNuxtPlugin(() => { }) ``` -:: - -### Vue - -Register the plugin when creating your head instance: - -::code-block - +#vue ```ts [main.ts] import { createHead } from '@unhead/vue/client' import { CanonicalPlugin } from 'unhead/plugins' @@ -278,14 +270,7 @@ const head = createHead({ app.use(head) ``` -:: - -### React - -Register the plugin in your app entry: - -::code-block - +#react ```tsx [app.tsx] import { createHead } from '@unhead/react/client' import { CanonicalPlugin } from 'unhead/plugins' @@ -299,14 +284,7 @@ const head = createHead({ }) ``` -:: - -### Svelte - -Register the plugin when creating the head instance in your entry file: - -::code-block - +#svelte ```ts [src/entry-client.ts] import { createHead, UnheadContextKey } from '@unhead/svelte/client' import { CanonicalPlugin } from 'unhead/plugins' @@ -317,14 +295,7 @@ head.use(CanonicalPlugin({ })) ``` -:: - -### Angular - -Register the plugin via `provideClientHead` options: - -::code-block - +#angular ```ts [app.config.ts] import { provideClientHead } from '@unhead/angular' import { CanonicalPlugin } from 'unhead/plugins' diff --git a/docs/head/1.guides/plugins/infer-seo-meta-tags.md b/docs/head/1.guides/plugins/infer-seo-meta-tags.md index d4ff4f59e..7348210fa 100644 --- a/docs/head/1.guides/plugins/infer-seo-meta-tags.md +++ b/docs/head/1.guides/plugins/infer-seo-meta-tags.md @@ -45,7 +45,7 @@ Add the plugin to your Unhead configuration: ::code-block ```ts [Input] -import { InferSeoMetaPlugin } from 'unhead/plugins' +import { InferSeoMetaPlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ @@ -98,7 +98,7 @@ Remove site name suffix from Open Graph titles: ::code-block ```ts [Input] -import { InferSeoMetaPlugin } from 'unhead/plugins' +import { InferSeoMetaPlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ diff --git a/docs/head/1.guides/plugins/minify.md b/docs/head/1.guides/plugins/minify.md index 0307c9bf9..9df7e9635 100644 --- a/docs/head/1.guides/plugins/minify.md +++ b/docs/head/1.guides/plugins/minify.md @@ -22,7 +22,7 @@ Register the plugin when creating your head instance: ::code-block ```ts [Input] -import { MinifyPlugin } from 'unhead/plugins' +import { MinifyPlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ @@ -84,7 +84,7 @@ Note: The `ssr:render` hook runs synchronously, so custom minifiers must be sync ## Build-Time Minification -For build-time minification of static string literals inside `useHead()` / `useServerHead()` calls, enable the `minify` option on the [unified Vite plugin](/docs/head/guides/advanced/vite-plugin). This runs at build time using heavier tools (rolldown/esbuild for JS, lightningcss for CSS) that never enter your SSR runtime bundle. +For build-time minification of static string literals inside `useHead()` / `useServerHead()` calls, enable the `minify` option on the [unified Vite plugin](/docs/head/guides/build-plugins/minify-transform). This runs at build time using heavier tools (rolldown/esbuild for JS, lightningcss for CSS) that never enter your SSR runtime bundle. ::code-block ```ts [vite.config.ts] @@ -121,7 +121,7 @@ The built-in minifiers are also available as standalone functions for use in cus ::code-block ```ts [Input] -import { minifyCSS, minifyJS, minifyJSON } from 'unhead/minify' +import { minifyCSS, minifyJS, minifyJSON } from '@unhead/dynamic-import/minify' const minifiedJS = minifyJS('// comment\nvar x = 1;') const minifiedCSS = minifyCSS('body { margin: 0; }') @@ -131,6 +131,6 @@ const minifiedJSON = minifyJSON('{ "name": "test" }') ## Related -- [Build Plugins](/docs/head/guides/advanced/vite-plugin) - Vite and Webpack build optimizations +- [Build Plugins](/docs/head/guides/build-plugins/overview) - Vite and Webpack build optimizations - [Validate Plugin](/docs/head/guides/plugins/validate) - Catch common SEO and head tag mistakes - [Inner Content](/docs/head/guides/core-concepts/inner-content) - Working with innerHTML and textContent diff --git a/docs/head/1.guides/plugins/validate.md b/docs/head/1.guides/plugins/validate.md index 89423003d..a778e662a 100644 --- a/docs/head/1.guides/plugins/validate.md +++ b/docs/head/1.guides/plugins/validate.md @@ -23,7 +23,7 @@ Register the plugin when you want head tag validation — it's fully tree-shakea ::code-block ```ts [Input] -import { ValidatePlugin } from 'unhead/plugins' +import { ValidatePlugin } from '@unhead/dynamic-import/plugins' const head = createHead({ plugins: [ diff --git a/docs/head/7.api/0.get-started/overview.md b/docs/head/7.api/0.get-started/overview.md index b9405bae7..599994b28 100644 --- a/docs/head/7.api/0.get-started/overview.md +++ b/docs/head/7.api/0.get-started/overview.md @@ -16,6 +16,10 @@ description: 'Unhead API reference for useHead(), useSeoMeta(), useScript() comp :: +## How do I create plugins? + +See the [Plugins API](/docs/head/api/plugins) for creating custom plugins with `defineHeadPlugin`. + ## What hooks can I use? ### Entry Hooks diff --git a/docs/head/7.api/composables/0.use-head.md b/docs/head/7.api/composables/0.use-head.md index e730adca4..73456c3f8 100644 --- a/docs/head/7.api/composables/0.use-head.md +++ b/docs/head/7.api/composables/0.use-head.md @@ -209,14 +209,14 @@ Works with all composables that accept `HeadEntryOptions` — `useHead()`, `useS ## Reactivity -::VueOnly - ### Automatic Reactivity The `useHead()`{lang="ts"} composable automatically integrates with your framework's reactivity system: +::FrameworkCode + +#vue ```ts -// Vue example with reactive data import { useHead } from '@unhead/dynamic-import' import { computed, ref } from 'vue' @@ -230,12 +230,54 @@ useHead({ }) ``` -Framework integrations like Vue automatically: +#react +```tsx +import { useHead } from '@unhead/dynamic-import' +import { useState } from 'react' + +function MyPage() { + const [title, setTitle] = useState('Dynamic Title') + + useHead({ + title: () => title, + meta: [ + { name: 'description', content: () => `Description for ${title}` } + ] + }) + + return
My Page
+} +``` + +#solid +```tsx +import { useHead } from '@unhead/dynamic-import' +import { createSignal } from 'solid-js' + +function MyPage() { + const [title, setTitle] = createSignal('Dynamic Title') + + useHead({ + title: () => title(), + meta: [ + { name: 'description', content: () => `Description for ${title()}` } + ] + }) + + return
My Page
+} +``` + +:: + +::VueOnly -- Track reactive data changes with `watchEffect` -- Resolve refs, computed props, and reactive objects -- Clean up head entries on component unmount -- Handle special cases like keep-alive components +Vue automatically: + +- Tracks reactive data changes with `watchEffect` +- Resolves refs, computed props, and reactive objects +- Cleans up head entries on component unmount +- Handles special cases like keep-alive components :: @@ -325,7 +367,7 @@ declare module '@unhead/schema' { ::code-group ```ts [Custom Link rel] -import type { GenericLink } from 'unhead/types' +import type { GenericLink } from '@unhead/dynamic-import/types' useHead({ link: [ @@ -336,7 +378,7 @@ useHead({ ``` ```ts [Custom Script type] -import type { GenericScript } from 'unhead/types' +import type { GenericScript } from '@unhead/dynamic-import/types' useHead({ script: [ @@ -486,16 +528,17 @@ This pattern is commonly used to implement layouts with defaults and page-specif ### Async Data Loading +::FrameworkCode + +#vue ```ts import { useHead } from '@unhead/dynamic-import' import { computed, ref } from 'vue' -// Initial setup const data = ref(null) const loading = ref(true) -const error = ref(null) -const headControl = useHead({ +useHead({ title: computed(() => data.value ? `${data.value.name} - Product` : loading.value @@ -503,21 +546,57 @@ const headControl = useHead({ : 'Product Not Found') }) -// Fetch data and update head async function fetchProduct(id) { - try { - loading.value = true - data.value = await api.getProduct(id) - } - catch (err) { - error.value = err - } - finally { - loading.value = false - } + loading.value = true + data.value = await api.getProduct(id) + loading.value = false } ``` +#react +```tsx +import { useHead } from '@unhead/dynamic-import' +import { useState } from 'react' + +function ProductPage({ id }) { + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + + useHead({ + title: () => data + ? `${data.name} - Product` + : loading + ? 'Loading...' + : 'Product Not Found' + }) + + return
Product Page
+} +``` + +#solid +```tsx +import { useHead } from '@unhead/dynamic-import' +import { createSignal } from 'solid-js' + +function ProductPage(props) { + const [data, setData] = createSignal(null) + const [loading, setLoading] = createSignal(true) + + useHead({ + title: () => data() + ? `${data().name} - Product` + : loading() + ? 'Loading...' + : 'Product Not Found' + }) + + return
Product Page
+} +``` + +:: + ::note This pattern works well with data fetching libraries and state management solutions. :: diff --git a/docs/head/7.api/composables/3.use-seo-meta.md b/docs/head/7.api/composables/3.use-seo-meta.md index a11b69ec3..f1639a50f 100644 --- a/docs/head/7.api/composables/3.use-seo-meta.md +++ b/docs/head/7.api/composables/3.use-seo-meta.md @@ -71,9 +71,12 @@ useSeoMeta({ ### Dynamic Meta Tags +::FrameworkCode + +#vue ```ts import { useSeoMeta } from '@unhead/dynamic-import' -import { computed } from 'vue' // or equivalent in your framework +import { computed, ref } from 'vue' const product = ref({ name: 'Awesome Product', @@ -88,6 +91,52 @@ useSeoMeta({ }) ``` +#react +```tsx +import { useSeoMeta } from '@unhead/dynamic-import' +import { useState } from 'react' + +function ProductPage() { + const [product] = useState({ + name: 'Awesome Product', + description: 'This product is amazing', + image: 'https://example.com/image.png' + }) + + useSeoMeta({ + title: () => `${product.name} - Your Brand`, + description: () => product.description, + ogImage: () => product.image, + }) + + return
Product Page
+} +``` + +#solid +```tsx +import { useSeoMeta } from '@unhead/dynamic-import' +import { createSignal } from 'solid-js' + +function ProductPage() { + const [product] = createSignal({ + name: 'Awesome Product', + description: 'This product is amazing', + image: 'https://example.com/image.png' + }) + + useSeoMeta({ + title: () => `${product().name} - Your Brand`, + description: () => product().description, + ogImage: () => product().image, + }) + + return
Product Page
+} +``` + +:: + ## How it works The `useSeoMeta` composable is powered by the [zhead](https://github.com/harlan-zw/zhead) schema and `unpackMeta` function. Unhead knows which meta tags belong where, as well as handling all the browser quirks for you. diff --git a/docs/head/7.api/plugins.md b/docs/head/7.api/plugins.md new file mode 100644 index 000000000..f85a88ae0 --- /dev/null +++ b/docs/head/7.api/plugins.md @@ -0,0 +1,88 @@ +--- +title: Plugins +description: Create custom plugins with defineHeadPlugin to hook into Unhead's tag resolution, DOM rendering, and SSR lifecycle. +--- + +Unhead uses a hooks based architecture powered by [unjs/hookable](https://github.com/unjs/hookable). Plugins let you tap into different parts of the head tag management lifecycle. + +## defineHeadPlugin + +A type helper for creating plugins. A plugin is an object with a `key` and optional `hooks`. + +```ts +import { defineHeadPlugin } from 'unhead/plugins' + +export const myPlugin = defineHeadPlugin({ + key: 'my-plugin', + hooks: { + 'tags:resolve': (ctx) => { + // modify ctx.tags before rendering + } + } +}) +``` + +Plugins can also be a function that receives the `Unhead` instance: + +```ts +import { defineHeadPlugin } from 'unhead/plugins' + +export const myPlugin = defineHeadPlugin((head) => ({ + key: 'my-plugin', + hooks: { + 'entries:updated': () => { + const tags = head.resolveTags() + console.log('Current tags:', tags) + } + } +})) +``` + +### Registering Plugins + +Pass plugins when creating the head instance, or add them later with `head.use()`. + +```ts +import { createHead } from 'unhead' + +const head = createHead({ + plugins: [myPlugin] +}) + +// or later +head.use(myPlugin) +``` + +## Custom Composables + +Unhead's composables are built on top of `useHead()`. You can create your own for common patterns. + +```ts +import { useHead } from 'unhead' + +export function useBodyClass(classes: string | string[]) { + const classList = Array.isArray(classes) ? classes : [classes] + return useHead({ + bodyAttrs: { + class: classList.join(' ') + } + }) +} +``` + +## Plugin Type + +```ts +type HeadPluginInput = + | (HeadPluginOptions & { key: string }) + | ((head: Unhead) => HeadPluginOptions & { key: string }) + +interface HeadPluginOptions { + hooks?: Record any> +} +``` + +## See Also + +- [Hooks API](/docs/head/api/hooks) for the full list of available hooks and their signatures +- [Built-in Plugins](/docs/head/guides/plugins) for plugins that ship with Unhead From 57083149d7d3e6bd4716fd7fe668eb2d5ab38a12 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Thu, 9 Apr 2026 02:05:32 +1000 Subject: [PATCH 4/7] doc: clean up --- .../guides/get-started/0.installation.md | 4 +- .../guides/get-started/0.installation.md | 4 +- .../guides/get-started/0.installation.md | 4 +- .../guides/0.get-started/0.installation.md | 4 +- .../head/1.guides/build-plugins/0.overview.md | 22 ++--- .../1.guides/build-plugins/1.tree-shaking.md | 6 +- .../build-plugins/2.seo-meta-transform.md | 6 +- .../build-plugins/3.minify-transform.md | 4 +- .../head/1.guides/build-plugins/4.devtools.md | 83 +++++++++++++++++++ docs/head/1.guides/plugins/minify.md | 4 +- 10 files changed, 112 insertions(+), 29 deletions(-) create mode 100644 docs/head/1.guides/build-plugins/4.devtools.md diff --git a/docs/0.react/schema-org/guides/get-started/0.installation.md b/docs/0.react/schema-org/guides/get-started/0.installation.md index 4eb3b0067..76427c0e2 100644 --- a/docs/0.react/schema-org/guides/get-started/0.installation.md +++ b/docs/0.react/schema-org/guides/get-started/0.installation.md @@ -71,10 +71,10 @@ If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview ```ts import react from '@vitejs/plugin-react' -import unhead from '@unhead/react/vite' +import { Unhead } from '@unhead/react/vite' export default defineConfig({ - plugins: [react(), unhead()], + plugins: [react(), Unhead()], }) ``` diff --git a/docs/0.solid-js/schema-org/guides/get-started/0.installation.md b/docs/0.solid-js/schema-org/guides/get-started/0.installation.md index a08c9bc8e..d4baf2508 100644 --- a/docs/0.solid-js/schema-org/guides/get-started/0.installation.md +++ b/docs/0.solid-js/schema-org/guides/get-started/0.installation.md @@ -121,10 +121,10 @@ If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview ```ts [vite.config.ts] import solid from 'vite-plugin-solid' -import unhead from '@unhead/solid-js/vite' +import { Unhead } from '@unhead/solid-js/vite' export default defineConfig({ - plugins: [unhead(), solid()], + plugins: [Unhead(), solid()], }) ``` diff --git a/docs/0.svelte/schema-org/guides/get-started/0.installation.md b/docs/0.svelte/schema-org/guides/get-started/0.installation.md index ca892b30b..44f4ed5d0 100644 --- a/docs/0.svelte/schema-org/guides/get-started/0.installation.md +++ b/docs/0.svelte/schema-org/guides/get-started/0.installation.md @@ -71,10 +71,10 @@ If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview ```ts import { svelte } from '@sveltejs/vite-plugin-svelte' -import unhead from '@unhead/svelte/vite' +import { Unhead } from '@unhead/svelte/vite' export default defineConfig({ - plugins: [unhead(), svelte()], + plugins: [Unhead(), svelte()], }) ``` diff --git a/docs/0.vue/schema-org/guides/0.get-started/0.installation.md b/docs/0.vue/schema-org/guides/0.get-started/0.installation.md index c6c2dd51c..fd89a8f9d 100644 --- a/docs/0.vue/schema-org/guides/0.get-started/0.installation.md +++ b/docs/0.vue/schema-org/guides/0.get-started/0.installation.md @@ -75,10 +75,10 @@ If you're using Vite, the [Vite plugin](/docs/head/guides/build-plugins/overview ```ts import vue from '@vitejs/plugin-vue' -import unhead from '@unhead/vue/vite' +import { Unhead } from '@unhead/vue/vite' export default defineConfig({ - plugins: [vue(), unhead()], + plugins: [vue(), Unhead()], }) ``` diff --git a/docs/head/1.guides/build-plugins/0.overview.md b/docs/head/1.guides/build-plugins/0.overview.md index a7e9fc2c9..7018fa35b 100644 --- a/docs/head/1.guides/build-plugins/0.overview.md +++ b/docs/head/1.guides/build-plugins/0.overview.md @@ -15,41 +15,41 @@ Install your framework's unhead package (if you haven't already), then add the p #vue ```ts [vite.config.ts] import vue from '@vitejs/plugin-vue' -import unhead from '@unhead/vue/vite' +import { Unhead } from '@unhead/vue/vite' export default defineConfig({ - plugins: [vue(), unhead()], + plugins: [vue(), Unhead()], }) ``` #react ```ts [vite.config.ts] import react from '@vitejs/plugin-react' -import unhead from '@unhead/react/vite' +import { Unhead } from '@unhead/react/vite' export default defineConfig({ - plugins: [react(), unhead()], + plugins: [react(), Unhead()], }) ``` #svelte ```ts [vite.config.ts] import { svelte } from '@sveltejs/vite-plugin-svelte' -import unhead from '@unhead/svelte/vite' +import { Unhead } from '@unhead/svelte/vite' export default defineConfig({ - plugins: [unhead(), svelte()], + plugins: [Unhead(), svelte()], }) ``` #solid ```ts [vite.config.ts] import solid from 'vite-plugin-solid' -import unhead from '@unhead/solid-js/vite' +import { Unhead } from '@unhead/solid-js/vite' export default defineConfig({ // Must come before solid() to see JSX before compilation - plugins: [unhead(), solid()], + plugins: [Unhead(), solid()], }) ``` @@ -72,7 +72,7 @@ The plugin combines several build-time optimizations: ## Options ```ts -unhead({ +Unhead({ // Disable server composable tree-shaking treeshake: false, // Disable useSeoMeta → useHead transform @@ -100,9 +100,9 @@ Every option accepts `false` to disable it entirely, or an object to configure i For Webpack projects, use `@unhead/bundler/webpack` directly: ```ts -import UnheadWebpack from '@unhead/bundler/webpack' +import { Unhead } from '@unhead/bundler/webpack' export default { - plugins: [UnheadWebpack()], + plugins: [Unhead()], } ``` diff --git a/docs/head/1.guides/build-plugins/1.tree-shaking.md b/docs/head/1.guides/build-plugins/1.tree-shaking.md index acdd0b6e6..fca50e78f 100644 --- a/docs/head/1.guides/build-plugins/1.tree-shaking.md +++ b/docs/head/1.guides/build-plugins/1.tree-shaking.md @@ -43,17 +43,17 @@ The transform only runs on client builds. SSR builds are left untouched. Tree-shaking is enabled by default when using the [unified Vite plugin](/docs/head/guides/build-plugins/overview): ```ts -import unhead from '@unhead/vue/vite' +import { Unhead } from '@unhead/vue/vite' export default defineConfig({ - plugins: [unhead()], + plugins: [Unhead()], }) ``` ### Disable ```ts -unhead({ +Unhead({ treeshake: false, }) ``` diff --git a/docs/head/1.guides/build-plugins/2.seo-meta-transform.md b/docs/head/1.guides/build-plugins/2.seo-meta-transform.md index 3774f1960..5fea38378 100644 --- a/docs/head/1.guides/build-plugins/2.seo-meta-transform.md +++ b/docs/head/1.guides/build-plugins/2.seo-meta-transform.md @@ -39,17 +39,17 @@ Import specifiers are also rewritten: `import { useSeoMeta } from 'unhead'` beco The transform is enabled by default when using the [unified Vite plugin](/docs/head/guides/build-plugins/overview): ```ts -import unhead from '@unhead/vue/vite' +import { Unhead } from '@unhead/vue/vite' export default defineConfig({ - plugins: [unhead()], + plugins: [Unhead()], }) ``` ### Disable ```ts -unhead({ +Unhead({ transformSeoMeta: false, }) ``` diff --git a/docs/head/1.guides/build-plugins/3.minify-transform.md b/docs/head/1.guides/build-plugins/3.minify-transform.md index 992eb4265..b05f4da2c 100644 --- a/docs/head/1.guides/build-plugins/3.minify-transform.md +++ b/docs/head/1.guides/build-plugins/3.minify-transform.md @@ -42,13 +42,13 @@ The transform automatically skips `application/json`, `application/ld+json`, `sp The minify transform is disabled by default. Enable it by providing minifier functions: ```ts -import unhead from '@unhead/vue/vite' +import { Unhead } from '@unhead/vue/vite' import { createJSMinifier } from '@unhead/bundler/minify/rolldown' import { createCSSMinifier } from '@unhead/bundler/minify/lightningcss' export default defineConfig({ plugins: [ - unhead({ + Unhead({ minify: { js: createJSMinifier(), css: createCSSMinifier(), diff --git a/docs/head/1.guides/build-plugins/4.devtools.md b/docs/head/1.guides/build-plugins/4.devtools.md new file mode 100644 index 000000000..071b494a6 --- /dev/null +++ b/docs/head/1.guides/build-plugins/4.devtools.md @@ -0,0 +1,83 @@ +--- +title: Devtools +description: Inspect head tags, SEO metadata, entries, and source locations in the Vite DevTools panel. Enabled by default in development. +navigation.title: Devtools +--- + +**Quick Answer:** The Unhead devtools integration adds a panel to [Vite DevTools](https://github.com/nicolo-ribaudo/vite-devtools) showing all active head tags, entries, SEO overview, plugins, and source file locations. Enabled by default in dev mode, zero config required. + +## What Does It Do? + +The devtools plugin provides a real-time view of your head state during development: + +- **Entries** — every `useHead()` / `useSeoMeta()` call with its source file and line number +- **Tags** — all resolved tags (meta, link, script, style, etc.) with their props, position, priority, and dedupe keys +- **SEO overview** — title, description, canonical, robots, Open Graph at a glance +- **Scripts** — tracked `useScript()` instances with their load status +- **Plugins** — which Unhead plugins are active +- **Template params** — current template parameter values and separator +- **Validation** — warnings from the [Validate plugin](/docs/head/guides/plugins/validate) (if enabled) + +### Source Tracing + +The plugin injects `_source` metadata (file path and line number) into every `useHead()`, `useSeoMeta()`, `useHeadSafe()`, and `useScript()` call at transform time. This lets you click through from a tag in the devtools panel to the exact line that created it. + +## Setup + +Devtools are enabled by default when using the [unified Vite plugin](/docs/head/guides/build-plugins/overview). They only run in `vite dev` (not in builds). + +::FrameworkCode + +#vue +```ts [vite.config.ts] +import vue from '@vitejs/plugin-vue' +import { Unhead } from '@unhead/vue/vite' + +export default defineConfig({ + plugins: [vue(), Unhead()], +}) +``` + +#react +```ts [vite.config.ts] +import react from '@vitejs/plugin-react' +import { Unhead } from '@unhead/react/vite' + +export default defineConfig({ + plugins: [react(), Unhead()], +}) +``` + +#nuxt +```ts +// Enabled automatically +``` + +:: + +### Disable + +```ts +Unhead({ + devtools: false, +}) +``` + +## Requirements + +The devtools panel requires the `@vitejs/devtools-kit` peer dependency and the `@unhead/devtools-app` package (which provides the UI). Both are optional; the plugin degrades gracefully if they're missing. + +## How It Works + +The integration has three parts: + +1. **Vite transform** — injects `_source` metadata into composable calls and wraps `createHead()` to expose the instance on `window.__unhead_devtools__` +2. **Bridge script** — a client-side module (`/@unhead/bridge.mjs`) that reads the head instance, serializes its state, and syncs it to the Vite DevTools shared state via `@vitejs/devtools-kit/client` +3. **DevTools panel** — a static app (`@unhead/devtools-app`) served at `/__unhead/` and registered as a Vite DevTools dock panel + +State is synced on every `dom:rendered` hook, so the panel updates in real time as head tags change. + +## See Also + +- [Build Plugins Overview](/docs/head/guides/build-plugins/overview) - All build-time optimizations +- [Validate Plugin](/docs/head/guides/plugins/validate) - Catch SEO and head tag issues (shown in devtools) diff --git a/docs/head/1.guides/plugins/minify.md b/docs/head/1.guides/plugins/minify.md index 9df7e9635..c5b350fc2 100644 --- a/docs/head/1.guides/plugins/minify.md +++ b/docs/head/1.guides/plugins/minify.md @@ -89,14 +89,14 @@ For build-time minification of static string literals inside `useHead()` / `useS ::code-block ```ts [vite.config.ts] import vue from '@vitejs/plugin-vue' -import unhead from '@unhead/vue/vite' +import { Unhead } from '@unhead/vue/vite' import { createJSMinifier } from '@unhead/bundler/minify/rolldown' import { createCSSMinifier } from '@unhead/bundler/minify/lightningcss' export default defineConfig({ plugins: [ vue(), - unhead({ + Unhead({ minify: { js: createJSMinifier(), css: createCSSMinifier(), From 98c0a90980ce65cb59e3de73bb5f0c5da9994450 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Thu, 9 Apr 2026 16:29:31 +1000 Subject: [PATCH 5/7] chore: progress --- .gitignore | 3 + docs/6.migration-guide/.navigation.yml | 0 docs/6.migration-guide/1.v3.md | 0 docs/{content => }/6.migration-guide/2.v2.md | 2 +- docs/content/6.migration-guide/1.v3.md | 428 - docs/content/7.releases/1.v3.md | 184 - docs/content/7.releases/2.v2.md | 189 - examples/angular/package.json | 28 +- .../vite-ssr-react-streaming/package.json | 6 +- examples/vite-ssr-react-ts/package.json | 6 +- .../vite-ssr-solidjs-streaming/package.json | 2 +- .../vite-ssr-svelte-streaming/package.json | 4 +- examples/vite-ssr-svelte/package.json | 4 +- examples/vite-ssr-ts/package.json | 2 +- examples/vite-ssr-vue-prerender/package.json | 2 +- .../vite-ssr-vue-prerender/vite.config.js | 4 +- examples/vite-ssr-vue-streaming/package.json | 3 +- .../vite-ssr-vue-streaming/vite.config.ts | 10 +- examples/vite-ssr-vue/package.json | 9 +- examples/vite-ssr-vue/src/App.vue | 65 +- .../src/components/HelloWorld.vue | 43 - examples/vite-ssr-vue/src/entry-client.ts | 6 +- examples/vite-ssr-vue/src/entry-server.ts | 17 +- examples/vite-ssr-vue/src/main.ts | 8 +- examples/vite-ssr-vue/src/pages/AboutPage.vue | 37 + examples/vite-ssr-vue/src/pages/BlogPage.vue | 90 + examples/vite-ssr-vue/src/pages/HomePage.vue | 89 + .../vite-ssr-vue/src/pages/ScriptsPage.vue | 52 + examples/vite-ssr-vue/src/router.ts | 33 + examples/vite-ssr-vue/vite.config.ts | 18 +- packages-aliased/addons/src/vite.ts | 4 +- packages/bundler/README.md | 8 +- packages/bundler/build.config.ts | 4 + packages/bundler/package.json | 2 + packages/bundler/src/devtools/bridge.ts | 388 + packages/bundler/src/devtools/plugin.ts | 75 + .../src/devtools}/rpc/functions/get-config.ts | 0 .../src => bundler/src/devtools}/rpc/index.ts | 0 .../src => bundler/src/devtools}/rpc/types.ts | 13 + .../src => bundler/src/devtools}/vite.ts | 76 +- packages/bundler/src/index.ts | 1 + .../src/unplugin/CreateHeadTransform.ts | 104 + .../bundler/src/unplugin/SSRStaticReplace.ts | 51 + .../src/unplugin/UseSeoMetaTransform.ts | 18 +- packages/bundler/src/unplugin/types.ts | 11 + packages/bundler/src/unplugin/vite.ts | 34 +- packages/bundler/src/unplugin/webpack.ts | 4 +- .../bundler/test/createHeadTransform.test.ts | 125 + .../bundler/test/useSeoMetaTransform.test.ts | 27 + packages/devtools-app/.nuxt/app.config.mjs | 4 +- packages/devtools-app/.nuxt/components.d.ts | 322 - packages/devtools-app/.nuxt/imports.d.ts | 49 - .../096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json | 1 - .../0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json | 1 - .../381b0558-98b7-448d-9168-38957e42e433.json | 1 - .../4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json | 1 - .../8251125c-23c7-4428-a540-3c662e362e93.json | 1 - .../c2629906-49d7-48f6-8234-c88fca0b6c44.json | 1 - .../cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json | 1 - .../.nuxt/nuxt-icon-server-bundle.mjs | 13 - packages/devtools-app/.nuxt/nuxt.d.ts | 24 - packages/devtools-app/.nuxt/nuxt.node.d.ts | 14 - .../.nuxt/prerender/chunks/_/error-500.mjs | 19 - .../prerender/chunks/_/error-500.mjs.map | 1 - .../.nuxt/prerender/chunks/_/renderer.mjs | 425 - .../.nuxt/prerender/chunks/_/renderer.mjs.map | 1 - .../chunks/build/client.precomputed.mjs | 4 - .../chunks/build/client.precomputed.mjs.map | 1 - .../.nuxt/prerender/chunks/nitro/nitro.mjs | 1596 -- .../prerender/chunks/nitro/nitro.mjs.map | 1 - .../chunks/virtual/_virtual_spa-template.mjs | 4 - .../virtual/_virtual_spa-template.mjs.map | 1 - .../devtools-app/.nuxt/prerender/index.mjs | 24 - .../.nuxt/prerender/index.mjs.map | 1 - packages/devtools-app/.nuxt/tsconfig.app.json | 232 - packages/devtools-app/.nuxt/tsconfig.json | 235 - .../devtools-app/.nuxt/tsconfig.node.json | 127 - .../devtools-app/.nuxt/tsconfig.server.json | 164 - .../devtools-app/.nuxt/tsconfig.shared.json | 164 - .../devtools-app/.nuxt/types/app.config.d.ts | 4 +- packages/devtools-app/.nuxt/types/build.d.ts | 25 - .../devtools-app/.nuxt/types/components.d.ts | 327 - .../devtools-app/.nuxt/types/imports.d.ts | 866 - .../devtools-app/.nuxt/types/middleware.d.ts | 7 - .../.nuxt/types/nitro-config.d.ts | 4 +- .../.nuxt/types/nitro-imports.d.ts | 149 - .../devtools-app/.nuxt/types/nitro-nuxt.d.ts | 64 - .../.nuxt/types/nitro-routes.d.ts | 17 - .../devtools-app/.nuxt/types/plugins.d.ts | 34 - packages/devtools-app/.nuxt/ui.css | 156 - .../devtools-app/.nuxt/ui/button-group.ts | 16 - packages/devtools-app/.output/nitro.json | 15 - packages/devtools-app/app.vue | 136 - packages/devtools-app/app/app.config.ts | 55 + packages/devtools-app/app/app.vue | 72 + .../devtools-app/app/assets/css/global.css | 126 + .../app/assets/fonts/fira-code.woff2 | 0 .../app/assets/fonts/hubot-sans.woff2 | 0 .../app/components/DevtoolsAlert.vue | 79 + .../app/components/DevtoolsCopyButton.vue | 21 + .../app/components/DevtoolsDocs.vue | 11 + .../app/components/DevtoolsEmptyState.vue | 28 + .../app/components/DevtoolsHeadEntry.vue | 35 + .../app/components/DevtoolsKeyValue.vue | 132 + .../app/components/DevtoolsLayout.vue | 80 + .../app/components/DevtoolsMetric.vue | 72 + .../app/components/DevtoolsPanel.vue | 64 + .../app/components/DevtoolsSection.vue | 93 + .../app/components/DevtoolsSnippet.vue | 39 + .../app/components/DevtoolsToolbar.vue | 33 + packages/devtools-app/app/components/Logo.vue | 68 + .../app/components/OCodeBlock.vue | 35 + .../devtools-app/app/composables/clipboard.ts | 4 + .../app/composables/link-checker.ts | 114 + .../devtools-app/{ => app}/composables/rpc.ts | 7 - .../devtools-app/app/composables/shiki.ts | 39 + .../{ => app}/composables/state.ts | 14 + .../devtools-app/app/composables/tools.ts | 133 + .../app/composables/update-check.ts | 20 + packages/devtools-app/app/pages/docs.vue | 3 + packages/devtools-app/app/pages/identity.vue | 475 + packages/devtools-app/app/pages/index.vue | 38 + packages/devtools-app/app/pages/schema.vue | 201 + packages/devtools-app/app/pages/scripts.vue | 277 + packages/devtools-app/app/pages/serp.vue | 1057 + packages/devtools-app/app/pages/social.vue | 234 + packages/devtools-app/app/public/logo.svg | 23 + .../{ => app}/utils/schema-validation.ts | 0 packages/devtools-app/assets/css/global.css | 27 - packages/devtools-app/composables/tools.ts | 28 - packages/devtools-app/nuxt.config.ts | 2 +- packages/devtools-app/package.json | 11 +- packages/devtools-app/pages/identity.vue | 475 - packages/devtools-app/pages/index.vue | 208 - packages/devtools-app/pages/schema.vue | 244 - packages/devtools-app/pages/scripts.vue | 106 - packages/devtools-app/pages/serp.vue | 182 - packages/devtools/build.config.ts | 18 - packages/devtools/package.json | 70 - packages/devtools/src/bridge.ts | 240 - packages/devtools/src/index.ts | 2 - packages/devtools/src/plugin.ts | 26 - packages/devtools/tsconfig.json | 8 - packages/react/src/composables.ts | 12 +- packages/react/src/vite.ts | 14 +- packages/schema-org/src/index.ts | 1 + packages/solid-js/src/vite.ts | 14 +- packages/svelte/src/vite.ts | 14 +- packages/unhead/src/plugins/validate.ts | 51 +- packages/unhead/src/scripts/types.ts | 4 + packages/unhead/src/scripts/useScript.ts | 12 +- .../unhead/test/unit/plugins/validate.test.ts | 48 +- packages/vue/src/utils.ts | 4 +- packages/vue/src/vite.ts | 14 +- pnpm-lock.yaml | 20001 ++++++++++------ pnpm-workspace.yaml | 43 +- tsconfig.json | 3 +- 157 files changed, 17860 insertions(+), 15246 deletions(-) create mode 100644 docs/6.migration-guide/.navigation.yml create mode 100644 docs/6.migration-guide/1.v3.md rename docs/{content => }/6.migration-guide/2.v2.md (97%) delete mode 100644 docs/content/6.migration-guide/1.v3.md delete mode 100644 docs/content/7.releases/1.v3.md delete mode 100644 docs/content/7.releases/2.v2.md delete mode 100644 examples/vite-ssr-vue/src/components/HelloWorld.vue create mode 100644 examples/vite-ssr-vue/src/pages/AboutPage.vue create mode 100644 examples/vite-ssr-vue/src/pages/BlogPage.vue create mode 100644 examples/vite-ssr-vue/src/pages/HomePage.vue create mode 100644 examples/vite-ssr-vue/src/pages/ScriptsPage.vue create mode 100644 examples/vite-ssr-vue/src/router.ts create mode 100644 packages/bundler/src/devtools/bridge.ts create mode 100644 packages/bundler/src/devtools/plugin.ts rename packages/{devtools/src => bundler/src/devtools}/rpc/functions/get-config.ts (100%) rename packages/{devtools/src => bundler/src/devtools}/rpc/index.ts (100%) rename packages/{devtools/src => bundler/src/devtools}/rpc/types.ts (77%) rename packages/{devtools/src => bundler/src/devtools}/vite.ts (60%) create mode 100644 packages/bundler/src/unplugin/CreateHeadTransform.ts create mode 100644 packages/bundler/src/unplugin/SSRStaticReplace.ts create mode 100644 packages/bundler/test/createHeadTransform.test.ts delete mode 100644 packages/devtools-app/.nuxt/components.d.ts delete mode 100644 packages/devtools-app/.nuxt/imports.d.ts delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json delete mode 100644 packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json delete mode 100644 packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs delete mode 100644 packages/devtools-app/.nuxt/nuxt.d.ts delete mode 100644 packages/devtools-app/.nuxt/nuxt.node.d.ts delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs.map delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/renderer.mjs delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/_/renderer.mjs.map delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/build/client.precomputed.mjs delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/build/client.precomputed.mjs.map delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/nitro/nitro.mjs delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/nitro/nitro.mjs.map delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/virtual/_virtual_spa-template.mjs delete mode 100644 packages/devtools-app/.nuxt/prerender/chunks/virtual/_virtual_spa-template.mjs.map delete mode 100644 packages/devtools-app/.nuxt/prerender/index.mjs delete mode 100644 packages/devtools-app/.nuxt/prerender/index.mjs.map delete mode 100644 packages/devtools-app/.nuxt/tsconfig.app.json delete mode 100644 packages/devtools-app/.nuxt/tsconfig.json delete mode 100644 packages/devtools-app/.nuxt/tsconfig.node.json delete mode 100644 packages/devtools-app/.nuxt/tsconfig.server.json delete mode 100644 packages/devtools-app/.nuxt/tsconfig.shared.json delete mode 100644 packages/devtools-app/.nuxt/types/build.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/components.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/imports.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/middleware.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/nitro-imports.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/nitro-nuxt.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/nitro-routes.d.ts delete mode 100644 packages/devtools-app/.nuxt/types/plugins.d.ts delete mode 100644 packages/devtools-app/.nuxt/ui.css delete mode 100644 packages/devtools-app/.nuxt/ui/button-group.ts delete mode 100644 packages/devtools-app/.output/nitro.json delete mode 100644 packages/devtools-app/app.vue create mode 100644 packages/devtools-app/app/app.config.ts create mode 100644 packages/devtools-app/app/app.vue create mode 100644 packages/devtools-app/app/assets/css/global.css create mode 100644 packages/devtools-app/app/assets/fonts/fira-code.woff2 create mode 100644 packages/devtools-app/app/assets/fonts/hubot-sans.woff2 create mode 100644 packages/devtools-app/app/components/DevtoolsAlert.vue create mode 100644 packages/devtools-app/app/components/DevtoolsCopyButton.vue create mode 100644 packages/devtools-app/app/components/DevtoolsDocs.vue create mode 100644 packages/devtools-app/app/components/DevtoolsEmptyState.vue create mode 100644 packages/devtools-app/app/components/DevtoolsHeadEntry.vue create mode 100644 packages/devtools-app/app/components/DevtoolsKeyValue.vue create mode 100644 packages/devtools-app/app/components/DevtoolsLayout.vue create mode 100644 packages/devtools-app/app/components/DevtoolsMetric.vue create mode 100644 packages/devtools-app/app/components/DevtoolsPanel.vue create mode 100644 packages/devtools-app/app/components/DevtoolsSection.vue create mode 100644 packages/devtools-app/app/components/DevtoolsSnippet.vue create mode 100644 packages/devtools-app/app/components/DevtoolsToolbar.vue create mode 100644 packages/devtools-app/app/components/Logo.vue create mode 100644 packages/devtools-app/app/components/OCodeBlock.vue create mode 100644 packages/devtools-app/app/composables/clipboard.ts create mode 100644 packages/devtools-app/app/composables/link-checker.ts rename packages/devtools-app/{ => app}/composables/rpc.ts (59%) create mode 100644 packages/devtools-app/app/composables/shiki.ts rename packages/devtools-app/{ => app}/composables/state.ts (83%) create mode 100644 packages/devtools-app/app/composables/tools.ts create mode 100644 packages/devtools-app/app/composables/update-check.ts create mode 100644 packages/devtools-app/app/pages/docs.vue create mode 100644 packages/devtools-app/app/pages/identity.vue create mode 100644 packages/devtools-app/app/pages/index.vue create mode 100644 packages/devtools-app/app/pages/schema.vue create mode 100644 packages/devtools-app/app/pages/scripts.vue create mode 100644 packages/devtools-app/app/pages/serp.vue create mode 100644 packages/devtools-app/app/pages/social.vue create mode 100644 packages/devtools-app/app/public/logo.svg rename packages/devtools-app/{ => app}/utils/schema-validation.ts (100%) delete mode 100644 packages/devtools-app/assets/css/global.css delete mode 100644 packages/devtools-app/composables/tools.ts delete mode 100644 packages/devtools-app/pages/identity.vue delete mode 100644 packages/devtools-app/pages/index.vue delete mode 100644 packages/devtools-app/pages/schema.vue delete mode 100644 packages/devtools-app/pages/scripts.vue delete mode 100644 packages/devtools-app/pages/serp.vue delete mode 100644 packages/devtools/build.config.ts delete mode 100644 packages/devtools/package.json delete mode 100644 packages/devtools/src/bridge.ts delete mode 100644 packages/devtools/src/index.ts delete mode 100644 packages/devtools/src/plugin.ts delete mode 100644 packages/devtools/tsconfig.json diff --git a/.gitignore b/.gitignore index 138a22d4b..35bfa1fa7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ playwright-report/ # Skilld references (recreated by `skilld install`) .skilld + +.nuxt +.output diff --git a/docs/6.migration-guide/.navigation.yml b/docs/6.migration-guide/.navigation.yml new file mode 100644 index 000000000..e69de29bb diff --git a/docs/6.migration-guide/1.v3.md b/docs/6.migration-guide/1.v3.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/content/6.migration-guide/2.v2.md b/docs/6.migration-guide/2.v2.md similarity index 97% rename from docs/content/6.migration-guide/2.v2.md rename to docs/6.migration-guide/2.v2.md index 07d5eb3ad..8c5e5f247 100644 --- a/docs/content/6.migration-guide/2.v2.md +++ b/docs/6.migration-guide/2.v2.md @@ -74,7 +74,7 @@ useHead(unheadInstance, { For legacy support with Vue Meta we allowed end users to provide deprecated properties: `vmid`, `hid`, `children` and `body`. -You must update these properties to the appropriate replacement or remove them. See the [v3 migration guide](/docs/content/migration-guide/v3#legacy-property-names) for the replacements. +You must update these properties to the appropriate replacement or remove them. See the [v3 migration guide](/docs/migration-guide/v3#legacy-property-names) for the replacements. --- diff --git a/docs/content/6.migration-guide/1.v3.md b/docs/content/6.migration-guide/1.v3.md deleted file mode 100644 index 1eaff922e..000000000 --- a/docs/content/6.migration-guide/1.v3.md +++ /dev/null @@ -1,428 +0,0 @@ ---- -title: "Migrate to v3" -description: "Migrate from Unhead v2 to v3. Covers all breaking changes, removed APIs, and their replacements." -navigation: - title: "v3" ---- - -Unhead v3 removes all deprecated APIs and focuses on performance improvements. This guide covers the breaking changes. - -## Automated Migration Checks - -Add `ValidatePlugin` during your upgrade to automatically detect v2 patterns and get actionable warnings: - -```ts -import { ValidatePlugin } from 'unhead/plugins' - -const head = createHead({ - plugins: [ - ValidatePlugin() // Detects deprecated props, missing plugins, and more - ] -}) -``` - -The plugin will warn you about: -- **Missing `TemplateParamsPlugin`** : template params like `%siteName` are now opt-in and will appear literally without the plugin -- **Missing `AliasSortingPlugin`** : `before:`/`after:` tag priorities are now opt-in and will be silently ignored without the plugin -- **Deprecated property names** : `children`, `hid`, `vmid`, `body: true` are no longer auto-converted -- **Removed `mode` option** : `{ mode: 'server' }` on `head.push()` is silently ignored - -All rules use ESLint-style config and can be individually disabled: - -```ts -ValidatePlugin({ - rules: { - 'missing-template-params-plugin': 'off', - } -}) -``` - -Remove `ValidatePlugin` once your migration is complete, or keep it for ongoing validation. - ---- - -## Legacy Property Names - -🚦 Impact Level: **High** - -The `DeprecationsPlugin` that automatically converted legacy property names has been removed. You must update your head entries to use the current property names. - -### `children` → `innerHTML` - -```diff -useHead({ - script: [{ -- children: 'console.log("hello")', -+ innerHTML: 'console.log("hello")', - }] -}) -``` - -### `hid` / `vmid` → `key` - -```diff -useHead({ - meta: [{ -- hid: 'description', -+ key: 'description', - name: 'description', - content: 'My description' - }] -}) -``` - -```diff -useHead({ - meta: [{ -- vmid: 'og:title', -+ key: 'og:title', - property: 'og:title', - content: 'My Title' - }] -}) -``` - -### `body: true` → `tagPosition: 'bodyClose'` - -```diff -useHead({ - script: [{ - src: '/script.js', -- body: true, -+ tagPosition: 'bodyClose', - }] -}) -``` - -### Quick Reference - -| Old Property | New Property | -|-------------|--------------| -| `children` | `innerHTML` | -| `hid` | `key` | -| `vmid` | `key` | -| `body: true` | `tagPosition: 'bodyClose'` | - ---- - -## Schema.org Plugin - -🚦 Impact Level: **High** - -The `PluginSchemaOrg` and `SchemaOrgUnheadPlugin` exports have been removed. Use `UnheadSchemaOrg` instead. - -```diff -- import { PluginSchemaOrg } from '@unhead/schema-org' -+ import { UnheadSchemaOrg } from '@unhead/schema-org' - -const head = createHead({ - plugins: [ -- PluginSchemaOrg() -+ UnheadSchemaOrg() - ] -}) -``` - -For Vue users: - -```diff -- import { PluginSchemaOrg } from '@unhead/schema-org/vue' -+ import { UnheadSchemaOrg } from '@unhead/schema-org/vue' -``` - -### Schema.org Config Options - -The following config options have been removed: - -| Removed Option | Replacement | -|---------------|-------------| -| `canonicalHost` | `host` | -| `canonicalUrl` | `path` + `host` | -| `position` | Use `tagPosition` on individual schema entries | -| `defaultLanguage` | Use `inLanguage` on schema nodes | -| `defaultCurrency` | Use `priceCurrency` on schema nodes | - -```diff -UnheadSchemaOrg({ -- canonicalHost: 'https://example.com', -- canonicalUrl: 'https://example.com/page', -+ host: 'https://example.com', -+ path: '/page', -}) -``` - ---- - -## Server Composables Removed - -🚦 Impact Level: **Medium-High** - -The `useServerHead`, `useServerHeadSafe`, and `useServerSeoMeta` composables have been removed. Use the standard composables instead. - -```diff -- import { useServerHead, useServerSeoMeta } from 'unhead' -+ import { useHead, useSeoMeta } from 'unhead' - -- useServerHead({ title: 'My Page' }) -+ useHead({ title: 'My Page' }) - -- useServerSeoMeta({ description: 'My description' }) -+ useSeoMeta({ description: 'My description' }) -``` - -If you need server-only head management, use conditional logic: - -```ts -if (import.meta.server) { - useHead({ title: 'Server Only' }) -} -``` - ---- - -## Core API Changes - -🚦 Impact Level: **Medium** - -### `createHeadCore` → `createUnhead` - -```diff -- import { createHeadCore } from 'unhead' -+ import { createUnhead } from 'unhead' - -- const head = createHeadCore() -+ const head = createUnhead() -``` - -### `headEntries()` → `entries` Map - -```diff -- const entries = head.headEntries() -+ const entries = [...head.entries.values()] -``` - -### `mode` Option Removed - -The `mode` option on head entries has been removed. Runtime mode detection is no longer supported. - -```diff -head.push({ - title: 'My Page', -- }, { mode: 'server' }) -+ }) -``` - -Use the appropriate `createHead` function instead: - -```ts -// Client-side -import { createHead } from 'unhead/client' - -// Server-side -import { createHead } from 'unhead/server' -``` - ---- - -## Vue Legacy Exports - -🚦 Impact Level: **Medium** - -### `/legacy` Export Path Deprecated - -The `@unhead/vue/legacy` import still works but emits a runtime deprecation warning. Update to the explicit client or server import: - -```diff -- import { createHead } from '@unhead/vue/legacy' -+ import { createHead } from '@unhead/vue/client' -// or for SSR -+ import { createHead } from '@unhead/vue/server' -``` - -### `createHeadCore` Removed - -```diff -- import { createHeadCore } from '@unhead/vue' -+ import { createHead } from '@unhead/vue/server' -// or for client -+ import { createHead } from '@unhead/vue/client' -``` - ---- - -## Server Utilities - -🚦 Impact Level: **Low** - -### `extractUnheadInputFromHtml` → `parseHtmlForUnheadExtraction` - -The function has been moved from `unhead/server` to `unhead/parser`. - -```diff -- import { extractUnheadInputFromHtml } from 'unhead/server' -+ import { parseHtmlForUnheadExtraction } from 'unhead/parser' - -- const { input } = extractUnheadInputFromHtml(html) -+ const { input } = parseHtmlForUnheadExtraction(html) -``` - ---- - -## Hooks - -🚦 Impact Level: **Low** - -The following hooks have been removed: - -- `init` : No longer needed -- `dom:renderTag` : DOM rendering is now synchronous -- `dom:rendered` : Use the `onRendered` option on `useHead()` instead - -The `dom:beforeRender` hook is now synchronous and `renderDOMHead` no longer returns a Promise: - -```diff -- await renderDOMHead(head, { document }) -+ renderDOMHead(head, { document }) -``` - -The SSR hooks (`ssr:beforeRender`, `ssr:render`, `ssr:rendered`) are now synchronous and `renderSSRHead` no longer returns a Promise: - -```diff -- const head = await renderSSRHead(head) -+ const head = renderSSRHead(head) -``` - ---- - -## Type Changes - -🚦 Impact Level: **Low** - -| Removed Type | Replacement | -|-------------|-------------| -| `Head` | `HeadTag` or specific tag types | -| `ResolvedHead` | `ResolvedHeadTag` | -| `MergeHead` | Use generics directly | -| `MetaFlatInput` | `MetaFlat` | -| `ResolvedMetaFlat` | `MetaFlat` | -| `RuntimeMode` | Removed (no replacement needed) | - -```diff -- import type { Head, MetaFlatInput, RuntimeMode } from 'unhead' -+ import type { HeadTag, MetaFlat } from 'unhead' -``` - ---- - -## Strict Type Narrowing for Link, Script, and Meta - -🚦 Impact Level: **Medium** - -The `Link` and `Script` types are now strict discriminated unions. Known `rel` and `type` values enforce per-tag required properties at the type level. `GenericLink` and `GenericScript` are still exported for custom values. - -### Link Tags - -Known `rel` values now enforce their required properties. For example, preloading a font requires `crossorigin`: - -```diff -useHead({ - link: [{ - rel: 'preload', - as: 'font', - href: '/font.woff2', -+ crossorigin: 'anonymous', // now required for font preloads - }] -}) -``` - -For custom `rel` values not in the known set, use `satisfies GenericLink`: - -```ts -import type { GenericLink } from 'unhead/types' - -useHead({ - link: [ - { rel: 'me', href: 'https://mastodon.social/@me' } satisfies GenericLink, - ] -}) -``` - -### Script Tags - -Inline scripts must have `textContent` or `innerHTML` and cannot include `src`, `async`, or `defer`. For custom `type` values, use `satisfies GenericScript`: - -```ts -import type { GenericScript } from 'unhead/types' - -useHead({ - script: [ - { type: 'text/plain', textContent: '...' } satisfies GenericScript, - ] -}) -``` - -### Meta Content Required - -Meta `content` is now required on name, property, and http-equiv meta tags. Use `null` explicitly to remove a meta tag: - -```diff -- useHead({ meta: [{ name: 'description' }] }) // no longer valid -+ useHead({ meta: [{ name: 'description', content: null }] }) // removes the tag -``` - -### String Variables - -When `rel` or `type` comes from a variable typed as `string`, TypeScript cannot narrow the union. Use `as const` or `satisfies`: - -```ts -import type { GenericLink } from 'unhead/types' - -const rel = getRelFromConfig() // string, not a literal -useHead({ - link: [{ rel, href: '/path' } satisfies GenericLink] -}) - -// or use as const for literals -const link = { rel: 'canonical' as const, href: '/path' } -useHead({ link: [link] }) -``` - ---- - -## Other Removed APIs - -- `resolveScriptKey` : Internal utility, no longer exported -- `DeprecationsPlugin` : Update property names directly instead -- `resolveUnrefHeadInput` (Vue) : Reactive resolution now happens automatically -- `setHeadInjectionHandler` (Vue) : Head injection is handled automatically - ---- - -## Quick Reference: Import Changes - -```diff -// Legacy properties - update property names directly, no plugin needed -- import { DeprecationsPlugin } from 'unhead/plugins' - -// Schema.org -- import { PluginSchemaOrg, SchemaOrgUnheadPlugin } from '@unhead/schema-org' -+ import { UnheadSchemaOrg } from '@unhead/schema-org' - -// Server composables -- import { useServerHead, useServerHeadSafe, useServerSeoMeta } from 'unhead' -+ import { useHead, useHeadSafe, useSeoMeta } from 'unhead' - -// Core -- import { createHeadCore } from 'unhead' -+ import { createUnhead } from 'unhead' - -// Server utilities -- import { extractUnheadInputFromHtml } from 'unhead/server' -+ import { parseHtmlForUnheadExtraction } from 'unhead/parser' - -// Vue -- import { createHeadCore, resolveUnrefHeadInput, setHeadInjectionHandler } from '@unhead/vue' -- import { ... } from '@unhead/vue/legacy' -+ import { createHead } from '@unhead/vue/client' -+ import { createHead } from '@unhead/vue/server' -``` diff --git a/docs/content/7.releases/1.v3.md b/docs/content/7.releases/1.v3.md deleted file mode 100644 index 9edd6f8b3..000000000 --- a/docs/content/7.releases/1.v3.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: "Unhead v3" -description: "Unhead v3 release notes: streaming SSR, synchronous rendering, and significant performance improvements." -navigation: - title: "v3" ---- - -Unhead v3 rebuilds the rendering engine from the ground up. The motivation: **streaming SSR**. Frameworks like Nuxt, SolidStart, and SvelteKit stream HTML to the browser as data loads, but head tags were still stuck in a request/response model, resolved once and never updated. To fix this properly, we had to make rendering synchronous, pluggable, and side-effect free. The result is a faster, smaller, and more capable head manager. - -## Highlights - -### Streaming SSR - -Head tags now update dynamically as suspense boundaries resolve during streaming. As each chunk streams to the browser, new ``, `<meta>`, and `<link>` tags are pushed to a client-side queue and applied to the DOM. No waiting for the full page to load. - -```ts -// entry-server.ts -import { createStreamableHead } from '@unhead/vue/stream/server' - -const { head, wrapStream } = createStreamableHead() -app.use(head) - -// wraps the Vue stream, injecting head updates as chunks resolve -return wrapStream(renderToWebStream(app), template) -``` - -```ts -// entry-client.ts -import { createStreamableHead } from '@unhead/vue/stream/client' - -const head = createStreamableHead() -app.use(head) -``` - -Under the hood: a queue stub (`window.__unhead__`) collects head entries as they stream in before the main JS bundle loads. Once the client head instance initializes, it processes the queue and takes over. No entries are ever lost regardless of timing. - -Streaming is supported for Vue, React, Solid.js, Svelte, and vanilla TypeScript. See [PR #537](https://github.com/unjs/unhead/pull/537). - -### Synchronous Rendering - -Both `renderDOMHead()` and `renderSSRHead()` are now **fully synchronous**. The async tag resolution pipeline has been replaced with a composable `resolveTags()` that resolves in a single pass, eliminating race conditions, unnecessary microtask scheduling, and `await` overhead. - -```diff -- await renderDOMHead(head, { document }) -+ renderDOMHead(head, { document }) -``` - -The head instance now exposes a pluggable `render()` function, so framework integrations can swap in their own rendering pipeline (DOM, SSR, or streaming) without modifying core. - -See PRs [#619](https://github.com/unjs/unhead/pull/619), [#622](https://github.com/unjs/unhead/pull/622), [#628](https://github.com/unjs/unhead/pull/628), [#629](https://github.com/unjs/unhead/pull/629), [#630](https://github.com/unjs/unhead/pull/630). - -### `useHead()` Type Narrowing - -`useHead()` now narrows types based on input. Link, script, and meta tags resolve to specific subtypes instead of a generic union, so you get precise autocomplete and type errors when something is wrong. - -```ts -useHead({ - link: [ - // Narrows to StylesheetLink: requires href, offers media, integrity, etc. - { rel: 'stylesheet', href: '/styles.css' }, - // Narrows to PreloadLink: requires as attribute - { rel: 'preload', as: 'font', href: '/font.woff2', crossorigin: 'anonymous' }, - ], - script: [ - // Narrows to ModuleScript - { src: '/app.mjs', type: 'module' }, - // Narrows to JsonLdScript - { type: 'application/ld+json', innerHTML: '{}' }, - ], -}) -``` - -See [PR #627](https://github.com/unjs/unhead/pull/627), [#665](https://github.com/unjs/unhead/pull/665). - -### ValidatePlugin - -New optional `ValidatePlugin` that inspects resolved head output and warns about common mistakes: missing titles, duplicate meta tags, contradictory preload priorities, and more. Fully tree-shakeable. Rules use ESLint-style flat config: - -```ts -import { ValidatePlugin } from 'unhead/plugins' - -createHead({ - plugins: [ - ValidatePlugin({ - rules: { - 'missing-description': 'off', - } - }) - ] -}) -``` - -See PRs [#690](https://github.com/unjs/unhead/pull/690), [#691](https://github.com/unjs/unhead/pull/691). - -### Canonical Plugin - -New built-in `CanonicalPlugin` that auto-generates `<link rel="canonical">` tags and resolves relative URLs to absolute in `og:image`, `twitter:image`, and `og:url`. Essential for SEO and social sharing. - -```ts -import { CanonicalPlugin } from 'unhead/plugins' - -createHead({ - plugins: [ - CanonicalPlugin({ canonicalHost: 'https://mysite.com' }) - ] -}) -``` - -See [PR #492](https://github.com/unjs/unhead/pull/492). - -## Performance - -| Build | v3 size | v2 size | Delta gz | -|-----------|---------|---------|---------------| -| client | 10254 | 11513 | **-534 (-11.2%)** | -| server | 9894 | 10361 | -194 (-4.6%) | -| vueClient | 11323 | 12567 | -533 (-10.2%) | -| vueServer | 10849 | 11312 | -191 (-4.1%) | - -| Benchmark | v2 mean | v3 mean | Delta | -|-------------|----------|----------|------------| -| @unhead/vue | 0.106ms | 0.072ms | **32% faster** | -| core | 0.088ms | 0.073ms | **17% faster** | - -Key optimizations: - -- Client-only CAPO sorting ([#626](https://github.com/unjs/unhead/pull/626)) -- Pure, tree-shakeable core with no side effects ([#632](https://github.com/unjs/unhead/pull/632)) -- Minified internal DOM state properties ([#635](https://github.com/unjs/unhead/pull/635)) -- Migrated unplugins from `estree-walker`/`acorn-loose` to `oxc-walker` ([#663](https://github.com/unjs/unhead/pull/663)) -- Walker-based `transformHtmlTemplate` ([#581](https://github.com/unjs/unhead/pull/581)) -- `TemplateParamsPlugin` and `AliasSortingPlugin` made opt-in for smaller bundles ([#493](https://github.com/unjs/unhead/pull/493), [#494](https://github.com/unjs/unhead/pull/494)) - -## Schema.org - -- **12 new nodes**: `Dataset`, `MusicAlbum`, `MusicGroup`, `MusicPlaylist`, `MusicRecording`, `PodcastEpisode`, `PodcastSeason`, `PodcastSeries`, `Service`, `TVEpisode`, `TVSeason`, `TVSeries` ([#612](https://github.com/unjs/unhead/pull/612)) -- **Graph resolution rewrite** for correctness and performance ([#616](https://github.com/unjs/unhead/pull/616)) -- **Removed `ohash` and `defu` dependencies** ([#605](https://github.com/unjs/unhead/pull/605)) - -## Other Changes - -- `useHeadSafe()` now whitelists CSS styles ([#491](https://github.com/unjs/unhead/pull/491)) -- Support for `blocking` attribute on scripts and stylesheets ([#489](https://github.com/unjs/unhead/pull/489)) -- `useScript()` consolidated back into core, legacy support dropped ([#498](https://github.com/unjs/unhead/pull/498)) -- `fediverse:creator` meta tag support ([#703](https://github.com/unjs/unhead/pull/703)) -- Switched from `hookable` to lighter `HookableCore` with sync-only hooks ([#631](https://github.com/unjs/unhead/pull/631)) -- Deprecation warnings added to aliased packages (`@unhead/schema`, `@unhead/shared`) ([#678](https://github.com/unjs/unhead/pull/678)) -- `templateParams` extensible via module augmentation ([#679](https://github.com/unjs/unhead/pull/679)) -- Respect user-provided `twitter:card` in `InferSeoMetaPlugin` ([#681](https://github.com/unjs/unhead/pull/681)) -- Enforce `as` attribute for preload links ([#683](https://github.com/unjs/unhead/pull/683)) - -## Bug Fixes - -- Hydration race condition with deferred patches ([#634](https://github.com/unjs/unhead/pull/634)) -- Process pending patches even when dirty is false ([#636](https://github.com/unjs/unhead/pull/636)) -- Deduplicate matching tags inside same render cycle ([#668](https://github.com/unjs/unhead/pull/668)) -- Dedupe `<link rel="alternate">` correctly ([#655](https://github.com/unjs/unhead/pull/655), [#656](https://github.com/unjs/unhead/pull/656), [#658](https://github.com/unjs/unhead/pull/658)) -- React: dispose head entries on unmount in StrictMode ([#664](https://github.com/unjs/unhead/pull/664)) -- React: force invalidation on entry disposal ([#559](https://github.com/unjs/unhead/pull/559)) -- Vue: support computed getter trigger ([#638](https://github.com/unjs/unhead/pull/638)) -- Vue: expose `@unhead/vue/stream/iife` with correct types ([#707](https://github.com/unjs/unhead/pull/707)) -- Scripts: prevent scope disposal from aborting unrelated trigger ([#660](https://github.com/unjs/unhead/pull/660)) -- Schema.org: allow `null` to opt out of default values ([#680](https://github.com/unjs/unhead/pull/680)) - -## Breaking Changes - -For the full migration guide, see [Migrate to v3](/docs/content/migration-guide/v3). - -### Summary - -| Old | New | -|-----|-----| -| `children` | `innerHTML` | -| `hid` / `vmid` | `key` | -| `body: true` | `tagPosition: 'bodyClose'` | -| `useServerHead` / `useServerSeoMeta` | `useHead` / `useSeoMeta` | -| `createHeadCore` | `createUnhead` | -| `@unhead/vue/legacy` | `@unhead/vue/client` or `@unhead/vue/server` | -| `mode` option on entries | Use client/server `createHead` imports | - -- `renderDOMHead()` and `renderSSRHead()` are now synchronous (remove `await`) -- CJS removed, all packages are ESM-only -- `TemplateParamsPlugin` and `AliasSortingPlugin` are no longer included by default -- `init`, `dom:renderTag`, `dom:rendered` hooks removed; `dom:beforeRender` is now synchronous diff --git a/docs/content/7.releases/2.v2.md b/docs/content/7.releases/2.v2.md deleted file mode 100644 index 6af264fff..000000000 --- a/docs/content/7.releases/2.v2.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -title: "Unhead v2: The full-stack <head> package for any framework." -description: "Unhead v2 is here! With first-class support for all major frameworks, a complete core rewrite, and a focus on performance, Unhead is the ultimate <head> manager." -publishedAt: "2024-03-24" -updatedAt: "2024-03-24" -navigation: - title: "v2" ---- - -I'm thrilled to announce the release of Unhead v2, a major milestone in Unhead becoming _the_ most performant and feature-complete head manager for _all reactive JavaScript frameworks_. - -## TL;DR - -- **Multi-framework Support**: Added support for [React](https://react.dev), [Svelte](https://svelte.dev), Solid.js, and [Angular](https://angular.dev) -- **Performance**: 51% faster SSR, 64% faster page switching, 21% smaller bundle size -- **Capo.js Integration**: Automatic tag sorting for optimal page loading -- **Leaner Package**: 14kb reduction in node_modules, single dependency -- **New Docs**: šŸ‘‹ - -## State of head management - -All JavaScript frameworks will run into the same challenge: how can they effectively manage tags and attributes outside the DOM entry -in a server-side rendered world? - -```html -<!DOCTYPE html> -<html> - <head> - <!-- JavaScript frameworks need to manage tags here server & client-side --> - </head> - <body> <!-- And probably body attributes --> - <!-- But also this --> - <div id="your-javascript-framework"></div> - <!-- and this... --> - <script src="/your-javascript-framework.js"></script> - </body> -</html> -``` - -As each framework has its unique constraints, they have all come up with their own solutions to this problem. Most of them converged -on a head provider pattern, where the framework allows you to wrap tags in a specific component (or provider) and they figure out -the rest. - -```vue [ComponentFoo] -<HeadProvider> - <title>My Page - - - -``` - -Seen as: ``{lang="html"}, ``{lang="html"}, ``{lang="html"} or more granular tags such as ``{lang="html"}, `<Meta>`{lang="html"}, `<Link>`{lang="html"}, etc. - -### Ecosystem - -Traditionally frameworks like React, [Vue](https://vuejs.org) and Angular have left it up to their respective ecosystems to solve this problem. Vue had [Vue Meta](https://vue-meta.nuxtjs.org/) and [VueUse Head](https://github.com/vueuse/head) and -React had and continues to have [React Helmet](https://github.com/nfl/react-helmet). - -We see some frameworks shifting towards providing simple support as part of their core, such as in [React v19](https://react.dev/blog/2024/12/05/react-19) -and in [Angular v14](https://angular.io/guide/releases#angular-v14-0-0). - -These solutions tend to be reliant on the component pattern, which is limited in its capabilities. - -## A quick recap on Unhead - -Unhead started out humbly 5 years ago as [`@vueuse/head`](https://github.com/vueuse/head). Since then it's joined -[UnJS](https://unjs.io/) as a high-quality, single-purpose package that works anywhere and is downloaded over ~100k times a day. - -:UnheadDownloads{class="my-10 lg:-mx-20 lg:w-[125%] rounded overflow-hidden"} - -Built from bullet-proof primitives such as `useHead()`{lang="ts"} and `useSeoMeta()`{lang="ts"}, Unhead is building out the ecosystem -of head management which is being realized through the v2 release. - -## v2 Release - -For the full changelog of changes please reference the [v2 roadmap](https://github.com/unjs/unhead/issues/395) GitHub Issue. - -### New Framework Supported - -Unhead started as a Vue-focused solution, but with v2, we've expanded to support all major frontend frameworks with -deep reactivity integrations. - -:FrameworkSelectorMinimal{ignore-redirect} - -```ts -import { useHead } from '@unhead/dynamic-import' - -useHead({ - title: 'šŸ‘‹ @FRAMEWORK_NAME@' -}) -``` - -- **Vue** : `@unhead/vue`: [Installation](/docs/vue/head/guides/get-started/installation) & [Reactivity Guide](/docs/vue/head/guides/core-concepts/reactivity-and-context) -- **React** : `@unhead/react`: [Installation](/docs/react/head/guides/get-started/installation) & [Reactivity Guide](/docs/react/head/guides/core-concepts/reactivity) -- **Svelte** : `@unhead/svelte`: [Installation](/docs/svelte/head/guides/get-started/installation) & [Reactivity Guide](/docs/svelte/head/guides/core-concepts/reactivity) -- **Solid.js** : `@unhead/solid-js`: [Installation](/docs/solid-js/head/guides/get-started/installation) & [Reactivity Guide](/docs/solid-js/head/guides/core-concepts/reactivity) -- **Angular** : `@unhead/angular`: [Installation](/docs/angular/head/guides/get-started/installation) & [Reactivity Guide](/docs/angular/head/guides/core-concepts/reactivity) - -Each framework integration provides the same powerful features with idiomatic patterns for that ecosystem. - -### ⚔ Runtime Performance Improvements - -Every tenth of a millisecond counts performance. In this release, much of the core has been rewritten to improve performance and tree-shakability. - -**Rewritten Core**: With the [core being rewritten](https://github.com/unjs/unhead/pull/488), optimizations were done -to improve the fast-path times. - -- āœ… SSR **+51% Faster** 0.34ms ⇢ 0.20ms ([Benchmark: Medium Site](https://github.com/unjs/unhead/blob/main/bench/ssr-harlanzw-com-e2e.bench.ts)) - -**Faster INP**: Tag resolves are now [cached between DOM renders](https://github.com/unjs/unhead/pull/504). For large sites, this _can_ lead to lower INP when switching between pages. - -- āœ… CSR Page Switching **+64% Faster** 0.43ms ⇢ 0.22ms ([Benchmark: 10 Page Changes x useHead()](https://github.com/unjs/unhead/blob/main/packages/unhead/test/bench/ssr-perf.bench.ts)) - -**Leaner Core**: Through aggressive code optimizations and moving some features to opt-in, we see an improvement in the bundle size. - -- āœ… Client: **21% smaller** - 13.6 kB (gz 5.3 kB) ⇢ 10.7 kB (gz 4.5 kB) ([Minimal: useHead() + createUnhead()](https://github.com/nuxt/nuxt/pull/31169/files#diff-04a7585c5d6ddd5cf80321c69bc6e07cd85e9e86ae35fa5f9c036651f137c312R26) ) -- āœ… Server: **22% smaller** - 10.3 kB (gz 4.1 kB) ⇢ 8 kB (gz 3.4 kB) ([Minimal: useHead() + createUnhead()](https://github.com/nuxt/nuxt/pull/31169/files#diff-04a7585c5d6ddd5cf80321c69bc6e07cd85e9e86ae35fa5f9c036651f137c312R26)) - -Benchmarks do not reflect real-world performance. - -:UnheadTwoGraphs - -### Capo.js Sorting - -Unhead v2 now applies capo.js sorting to all tags by default which provides optimizations to improve the -performance of your site for end users. This sorting follows best practices for resource loading order in the document head. - -:CapoExample - -### šŸ“¦ Bundle Improvements - -**Single dependency** Unhead now relies on a single dependency, [hookable](https://github.com/unjs/hookable), used for pluggability. - -- āœ… **5 fewer** dependencies - -**ESM & dropped workspace packages** Only ESM is now published, workspace packages are deprecated for subpath exports. - -- āœ… **14kb reduction** in `node_modules` - -## šŸ”„ Upgrading to v2 - -The migration path is straightforward; Unhead provides a `legacy` subpath build to ease the transition. - -See the [Migrate to v2](/docs/content/migration-guide/v2) guide. - -## šŸ”® The Future - -Now that Unhead has a battle-tested core and supports all major frameworks, we can start to build out the ecosystem of head management. Here's what's on our roadmap: - -### Short Term: Improved Tag Validation - -For several tags, the spec requires certain attributes when you use another attribute. For example, the `<link>`{lang="html"} tag -only requires an `as` attribute when you use `rel="preload"`{lang="html"}. - -The type system does not enforce this to avoid potential type juggling; however, it can lead to mistakes. - -To catch broken tags we have several options: -- Improve the type system for optionally required attributes -- Implement [ESLint](https://eslint.org) rules to catch anything the types haven't -- Use a runtime plugin in development to validate resolved tags - -### Medium Term: Third-Party Scripts - -While Unhead already has a lower-level way to work with third-party scripts `useScript()`{lang="ts"}, we can make them easier and more secure -to use by introducing higher-level composables, such as `useGoogleAnalytics()`{lang="ts"}, `useFacebookPixel()`{lang="ts"}, etc. - -These would hook into framework lifecycles to ensure optimal performance and security while providing improved developer experience. - -```ts -const { proxy } = useGoogleAnalytics({ - id: 'UA-123456789', -}) - -proxy.gtag('page_view', { - page_path: '/my-page', -}) -``` - -### Exploratory: OG Image - -Turn HTML templates into OG images with a simple composable. - -```ts -useOgImage('<div class="bg-red-500">my image :)</div>') -``` - -## šŸ™ Thank You - -This release wouldn't be possible without our amazing community. Special thanks to all contributors who helped with code, testing, and documentation. diff --git a/examples/angular/package.json b/examples/angular/package.json index f778a9e82..e50114efc 100644 --- a/examples/angular/package.json +++ b/examples/angular/package.json @@ -12,16 +12,16 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.7", - "@angular/common": "^21.2.7", - "@angular/compiler": "^21.2.7", - "@angular/core": "^21.2.7", - "@angular/forms": "^21.2.7", - "@angular/platform-browser": "^21.2.7", - "@angular/platform-browser-dynamic": "^21.2.7", - "@angular/platform-server": "^21.2.7", - "@angular/router": "^21.2.7", - "@angular/ssr": "^21.2.6", + "@angular/animations": "^21.2.8", + "@angular/common": "^21.2.8", + "@angular/compiler": "^21.2.8", + "@angular/core": "^21.2.8", + "@angular/forms": "^21.2.8", + "@angular/platform-browser": "^21.2.8", + "@angular/platform-browser-dynamic": "^21.2.8", + "@angular/platform-server": "^21.2.8", + "@angular/router": "^21.2.8", + "@angular/ssr": "^21.2.7", "@unhead/angular": "workspace:*", "express": "^5.2.1", "rxjs": "~7.8.2", @@ -29,10 +29,10 @@ "zone.js": "~0.16.1" }, "devDependencies": { - "@angular-devkit/build-angular": "^21.2.6", - "@angular/build": "^21.2.6", - "@angular/cli": "^21.2.6", - "@angular/compiler-cli": "^21.2.7", + "@angular-devkit/build-angular": "^21.2.7", + "@angular/build": "^21.2.7", + "@angular/cli": "^21.2.7", + "@angular/compiler-cli": "^21.2.8", "@types/express": "^5.0.6", "@types/node": "^25.5.2", "ng-packagr": "^21.2.2", diff --git a/examples/vite-ssr-react-streaming/package.json b/examples/vite-ssr-react-streaming/package.json index 73464de0b..0ce15c3ca 100644 --- a/examples/vite-ssr-react-streaming/package.json +++ b/examples/vite-ssr-react-streaming/package.json @@ -16,8 +16,8 @@ "@unhead/react": "workspace:*", "compression": "^1.8.1", "express": "^5.2.1", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.5", + "react-dom": "^19.2.5", "react-router-dom": "^7.14.0" }, "devDependencies": { @@ -29,6 +29,6 @@ "@vitejs/plugin-react": "^6.0.1", "cross-env": "^10.1.0", "typescript": "5.8.3", - "vite": "^8.0.5" + "vite": "^8.0.8" } } diff --git a/examples/vite-ssr-react-ts/package.json b/examples/vite-ssr-react-ts/package.json index 67f2af7fd..e4f0ec0ef 100644 --- a/examples/vite-ssr-react-ts/package.json +++ b/examples/vite-ssr-react-ts/package.json @@ -15,8 +15,8 @@ "@unhead/schema-org": "workspace:*", "compression": "^1.8.1", "express": "^5.2.1", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.5", + "react-dom": "^19.2.5", "react-router-dom": "^7.14.0", "sirv": "^3.0.2" }, @@ -28,6 +28,6 @@ "@vitejs/plugin-react": "^6.0.1", "cross-env": "^10.1.0", "typescript": "5.8.3", - "vite": "^8.0.5" + "vite": "^8.0.8" } } diff --git a/examples/vite-ssr-solidjs-streaming/package.json b/examples/vite-ssr-solidjs-streaming/package.json index e8ae0145a..ec173b08e 100644 --- a/examples/vite-ssr-solidjs-streaming/package.json +++ b/examples/vite-ssr-solidjs-streaming/package.json @@ -24,7 +24,7 @@ "@types/node": "^25.5.2", "cross-env": "^10.1.0", "typescript": "5.8.3", - "vite": "^8.0.5", + "vite": "^8.0.8", "vite-plugin-solid": "^2.11.12" } } diff --git a/examples/vite-ssr-svelte-streaming/package.json b/examples/vite-ssr-svelte-streaming/package.json index 4152c81c8..783ee451d 100644 --- a/examples/vite-ssr-svelte-streaming/package.json +++ b/examples/vite-ssr-svelte-streaming/package.json @@ -24,9 +24,9 @@ "@types/express": "^5.0.6", "@types/node": "^25.5.2", "cross-env": "^10.1.0", - "svelte": "^5.55.1", + "svelte": "^5.55.2", "tslib": "^2.8.1", "typescript": "5.8.3", - "vite": "^8.0.5" + "vite": "^8.0.8" } } diff --git a/examples/vite-ssr-svelte/package.json b/examples/vite-ssr-svelte/package.json index a13acc682..66f3addb4 100644 --- a/examples/vite-ssr-svelte/package.json +++ b/examples/vite-ssr-svelte/package.json @@ -24,10 +24,10 @@ "@types/express": "^5.0.6", "@types/node": "^25.5.2", "cross-env": "^10.1.0", - "svelte": "^5.55.1", + "svelte": "^5.55.2", "svelte-check": "^4.4.6", "tslib": "^2.8.1", "typescript": "5.8.3", - "vite": "^8.0.5" + "vite": "^8.0.8" } } diff --git a/examples/vite-ssr-ts/package.json b/examples/vite-ssr-ts/package.json index 9451589be..973ff97d0 100644 --- a/examples/vite-ssr-ts/package.json +++ b/examples/vite-ssr-ts/package.json @@ -20,6 +20,6 @@ "@types/node": "^25.5.2", "cross-env": "^10.1.0", "typescript": "5.8.3", - "vite": "^8.0.5" + "vite": "^8.0.8" } } diff --git a/examples/vite-ssr-vue-prerender/package.json b/examples/vite-ssr-vue-prerender/package.json index 4b7d8d6d3..ddb729162 100644 --- a/examples/vite-ssr-vue-prerender/package.json +++ b/examples/vite-ssr-vue-prerender/package.json @@ -19,7 +19,7 @@ "pinia": "^3.0.4", "unhead": "workspace:*", "unplugin-auto-import": "^21.0.0", - "vite": "^8.0.5", + "vite": "^8.0.8", "vue-router": "^5.0.4" }, "devDependencies": { diff --git a/examples/vite-ssr-vue-prerender/vite.config.js b/examples/vite-ssr-vue-prerender/vite.config.js index 9d0d7cef7..d7e12b474 100644 --- a/examples/vite-ssr-vue-prerender/vite.config.js +++ b/examples/vite-ssr-vue-prerender/vite.config.js @@ -3,7 +3,7 @@ import vuePlugin from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import AutoImport from 'unplugin-auto-import/vite' import { unheadVueComposablesImports } from '@unhead/vue' -import UnheadPlugin from '@unhead/bundler/vite' +import { Unhead } from '@unhead/vue/vite' import Inspect from 'vite-plugin-inspect' const virtualFile = '@virtual-file' @@ -30,7 +30,7 @@ export default defineConfig(({ command, ssrBuild }) => ({ build: true, outputDir: '.vite-inspect' }), - UnheadPlugin(), + Unhead(), vuePlugin(), vueJsx(), { diff --git a/examples/vite-ssr-vue-streaming/package.json b/examples/vite-ssr-vue-streaming/package.json index 077c56450..3b4fb4ed5 100644 --- a/examples/vite-ssr-vue-streaming/package.json +++ b/examples/vite-ssr-vue-streaming/package.json @@ -24,9 +24,10 @@ "@playwright/test": "^1.59.1", "@types/express": "^5.0.6", "@types/node": "^25.5.2", + "@vitejs/devtools": "^0.1.13", "@vitejs/plugin-vue": "latest", "cross-env": "^10.1.0", "typescript": "5.8.3", - "vite": "^8.0.5" + "vite": "^8.0.8" } } diff --git a/examples/vite-ssr-vue-streaming/vite.config.ts b/examples/vite-ssr-vue-streaming/vite.config.ts index f890f0b71..e1145279f 100644 --- a/examples/vite-ssr-vue-streaming/vite.config.ts +++ b/examples/vite-ssr-vue-streaming/vite.config.ts @@ -1,12 +1,18 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' -import unhead from '@unhead/vue/vite' +import { DevTools } from '@vitejs/devtools' +import { Unhead } from '@unhead/vue/vite' export default defineConfig({ plugins: [ + DevTools(), vue(), - unhead({ streaming: true }), + Unhead({ streaming: true }), ], + devtools: { + enabled: true, + clientAuth: false, + }, build: { minify: false, }, diff --git a/examples/vite-ssr-vue/package.json b/examples/vite-ssr-vue/package.json index 1e3626147..1623641ff 100644 --- a/examples/vite-ssr-vue/package.json +++ b/examples/vite-ssr-vue/package.json @@ -12,21 +12,26 @@ "check": "vue-tsc" }, "dependencies": { + "@unhead/schema-org": "workspace:*", "@unhead/vue": "workspace:*", "compression": "^1.8.1", "express": "^5.2.1", "rollup-plugin-visualizer": "^7.0.1", "sirv": "^3.0.2", "vite-bundle-analyzer": "^1.3.7", - "vue": "^3.5.32" + "vue": "^3.5.32", + "vue-router": "^5.0.4" }, "devDependencies": { "@types/express": "^5.0.6", "@types/node": "^25.5.2", + "@unhead/bundler": "workspace:*", + "@vitejs/devtools": "^0.1.13", "@vitejs/plugin-vue": "^6.0.5", "cross-env": "^10.1.0", "typescript": "5.8.3", - "vite": "^8.0.5", + "unplugin-auto-import": "^21.0.0", + "vite": "^8.0.8", "vue-tsc": "^3.2.6" } } diff --git a/examples/vite-ssr-vue/src/App.vue b/examples/vite-ssr-vue/src/App.vue index 7f29dec28..8d051dc09 100644 --- a/examples/vite-ssr-vue/src/App.vue +++ b/examples/vite-ssr-vue/src/App.vue @@ -1,36 +1,57 @@ <script setup lang="ts"> -// This starter template is using Vue 3 <script setup> SFCs -// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup -import HelloWorld from './components/HelloWorld.vue' -import { useHead } from '@unhead/vue' +import { RouterLink, RouterView } from 'vue-router' useHead({ - title: 'hello from app.vue' + titleTemplate: '%s | Unhead Demo', + templateParams: { + separator: '|', + }, + htmlAttrs: { lang: 'en' }, + meta: [ + { charset: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1' }, + { name: 'color-scheme', content: 'light dark' }, + { name: 'theme-color', content: '#4f46e5', media: '(prefers-color-scheme: light)' }, + { name: 'theme-color', content: '#818cf8', media: '(prefers-color-scheme: dark)' }, + ], + link: [ + { rel: 'icon', type: 'image/svg+xml', href: '/vite.svg' }, + { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }, + { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }, + ], }) </script> <template> - <div> - <a href="https://vite.dev" target="_blank"> - <img src="/vite.svg" class="logo" alt="Vite logo" /> - </a> - <a href="https://vuejs.org/" target="_blank"> - <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> - </a> - </div> - <HelloWorld msg="Vite + Vue" /> +<div id="layout"> + <nav class="site-nav"> + <RouterLink to="/">Home</RouterLink> + <RouterLink to="/about">About</RouterLink> + <RouterLink to="/blog">Blog</RouterLink> + <RouterLink to="/scripts">Scripts</RouterLink> + </nav> + <main> + <RouterView /> + </main> +</div> </template> <style scoped> -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; +.site-nav { + display: flex; + gap: 1rem; + padding: 1rem; + border-bottom: 1px solid #e5e7eb; } -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); +.site-nav a { + color: #4f46e5; + text-decoration: none; + font-weight: 500; } -.logo.vue:hover { - filter: drop-shadow(0 0 2em #42b883aa); +.site-nav a:hover { + text-decoration: underline; +} +main { + padding: 2rem; } </style> diff --git a/examples/vite-ssr-vue/src/components/HelloWorld.vue b/examples/vite-ssr-vue/src/components/HelloWorld.vue deleted file mode 100644 index 5f8819854..000000000 --- a/examples/vite-ssr-vue/src/components/HelloWorld.vue +++ /dev/null @@ -1,43 +0,0 @@ -<script setup lang="ts"> -import { ref } from 'vue' -import { useHead } from '@unhead/vue' - -defineProps<{ msg: string }>() - -const count = ref(0) - -useHead({ - title: () => count.value ? `Count: ${count.value}` : undefined, -}) -</script> - -<template> -<h1>{{ msg }}</h1> - -<div class="card"> - <button type="button" @click="count++">count is {{ count }}</button> - <p> - Edit - <code>components/HelloWorld.vue</code> to test HMR - </p> -</div> - -<p> - Check out - <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank" - >create-vue</a - >, the official Vue + Vite starter -</p> -<p> - Install - <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a> - in your IDE for a better DX -</p> -<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p> -</template> - -<style scoped> -.read-the-docs { - color: #888; -} -</style> diff --git a/examples/vite-ssr-vue/src/entry-client.ts b/examples/vite-ssr-vue/src/entry-client.ts index 4ddd79122..075826651 100644 --- a/examples/vite-ssr-vue/src/entry-client.ts +++ b/examples/vite-ssr-vue/src/entry-client.ts @@ -2,8 +2,10 @@ import './style.css' import { createApp } from './main' import { createHead } from '@unhead/vue/client' -const { app } = createApp() +const { app, router } = createApp() const head = createHead() app.use(head) -app.mount('#app') +router.isReady().then(() => { + app.mount('#app') +}) diff --git a/examples/vite-ssr-vue/src/entry-server.ts b/examples/vite-ssr-vue/src/entry-server.ts index f3ee11424..4b64c7c1f 100644 --- a/examples/vite-ssr-vue/src/entry-server.ts +++ b/examples/vite-ssr-vue/src/entry-server.ts @@ -1,19 +1,16 @@ import { renderToString } from 'vue/server-renderer' import { createApp } from './main' import { createHead } from '@unhead/vue/server' -import { useSeoMeta } from '@unhead/vue' -export async function render(_url: string) { - const { app } = createApp() +export async function render(url: string) { + const { app, router } = createApp() const head = createHead() app.use(head) - // no client-side hydration needed - useSeoMeta({ - title: 'My Awesome Site', - description: 'My awesome site description', - }, { head }) - const ctx = {} - const html = await renderToString(app, ctx) + + router.push(url) + await router.isReady() + + const html = await renderToString(app) return { html, head } } diff --git a/examples/vite-ssr-vue/src/main.ts b/examples/vite-ssr-vue/src/main.ts index ff091f8e0..0c0587f6f 100644 --- a/examples/vite-ssr-vue/src/main.ts +++ b/examples/vite-ssr-vue/src/main.ts @@ -1,10 +1,10 @@ import { createSSRApp } from 'vue' import App from './App.vue' +import { createRouter } from './router' -// SSR requires a fresh app instance per request, therefore we export a function -// that creates a fresh app instance. If using Vuex, we'd also be creating a -// fresh store here. export function createApp() { const app = createSSRApp(App) - return { app } + const router = createRouter() + app.use(router) + return { app, router } } diff --git a/examples/vite-ssr-vue/src/pages/AboutPage.vue b/examples/vite-ssr-vue/src/pages/AboutPage.vue new file mode 100644 index 000000000..6b4b82f70 --- /dev/null +++ b/examples/vite-ssr-vue/src/pages/AboutPage.vue @@ -0,0 +1,37 @@ +<script setup lang="ts"> +useHead({ + title: 'About', + link: [ + { rel: 'canonical', href: 'https://example.com/about' }, + ], +}) + +useSeoMeta({ + description: 'Learn more about the Unhead demo site and how it showcases head management features.', + ogTitle: 'About the Unhead Demo', + ogDescription: 'Learn more about the Unhead demo site.', + ogUrl: 'https://example.com/about', +}) + +useSchemaOrg([ + defineWebPage({ + '@type': 'AboutPage', + name: 'About the Unhead Demo', + description: 'Learn more about the Unhead demo site and how it showcases head management features.', + }), + defineBreadcrumb({ + itemListElement: [ + { name: 'Home', item: 'https://example.com/' }, + { name: 'About' }, + ], + }), +]) +</script> + +<template> + <div> + <h1>About</h1> + <p>This page exercises the Identity and Tags devtools tabs.</p> + <p>Check the Identity tab to see favicons, theme colors, and color scheme metadata configured in App.vue.</p> + </div> +</template> diff --git a/examples/vite-ssr-vue/src/pages/BlogPage.vue b/examples/vite-ssr-vue/src/pages/BlogPage.vue new file mode 100644 index 000000000..8e4afd0f1 --- /dev/null +++ b/examples/vite-ssr-vue/src/pages/BlogPage.vue @@ -0,0 +1,90 @@ +<script setup lang="ts"> +useHead({ + title: 'Blog Post: Getting Started with Unhead', + link: [ + { rel: 'canonical', href: 'https://example.com/blog' }, + ], +}) + +useSchemaOrg([ + defineArticle({ + '@type': 'BlogPosting', + 'headline': 'Getting Started with Unhead', + 'description': 'Learn how to manage document head tags with Unhead in your Vue application.', + 'datePublished': '2025-01-15', + 'dateModified': '2025-03-01', + 'image': 'https://unhead.unjs.io/og.png', + 'author': [ + definePerson({ + name: 'Harlan Wilton', + url: 'https://harlanzw.com', + }), + ], + }), + defineBreadcrumb({ + itemListElement: [ + { name: 'Home', item: 'https://example.com/' }, + { name: 'Blog' }, + ], + }), + defineQuestion({ + name: 'What is Unhead?', + acceptedAnswer: 'Unhead is a document head manager for any JavaScript framework.', + }), + defineQuestion({ + name: 'Does Unhead support SSR?', + acceptedAnswer: 'Yes, Unhead has first-class SSR support with streaming capabilities.', + }), +]) + +useSeoMeta({ + description: 'Learn how to manage document head tags with Unhead in your Vue application.', + ogTitle: 'Getting Started with Unhead', + ogDescription: 'Learn how to manage document head tags with Unhead.', + ogType: 'article', + ogUrl: 'https://example.com/blog', + ogImage: 'https://unhead.unjs.io/og.png', + articlePublishedTime: '2025-01-15', + articleModifiedTime: '2025-03-01', + articleAuthor: 'Harlan Wilton', +}) +</script> + +<template> + <div> + <h1>Getting Started with Unhead</h1> + <p class="meta">Published Jan 15, 2025 by Harlan Wilton</p> + + <article> + <p>This page demonstrates Schema.org structured data in the devtools.</p> + <p>Open the Schema.org tab to see BlogPosting, WebSite, BreadcrumbList, and FAQPage nodes.</p> + + <h2>FAQ</h2> + <dl> + <dt>What is Unhead?</dt> + <dd>Unhead is a document head manager for any JavaScript framework.</dd> + <dt>Does Unhead support SSR?</dt> + <dd>Yes, Unhead has first class SSR support with streaming capabilities.</dd> + </dl> + </article> + </div> +</template> + +<style scoped> +.meta { + color: #6b7280; + font-size: 0.875rem; +} +article { + margin-top: 1.5rem; + line-height: 1.7; +} +dt { + font-weight: 600; + margin-top: 1rem; +} +dd { + margin-left: 0; + color: #4b5563; +} +</style> diff --git a/examples/vite-ssr-vue/src/pages/HomePage.vue b/examples/vite-ssr-vue/src/pages/HomePage.vue new file mode 100644 index 000000000..5a416c6ed --- /dev/null +++ b/examples/vite-ssr-vue/src/pages/HomePage.vue @@ -0,0 +1,89 @@ +<script setup lang="ts"> +const count = ref(0) + +useHead({ + title: 'Home', + link: [ + { rel: 'canonical', href: 'https://example.com/' }, + ], +}) + +useSeoMeta({ + description: 'A demo site showcasing Unhead for Vue with SSR, featuring SEO meta tags, social sharing, and structured data.', + ogTitle: 'Unhead Demo: Head Management for Vue', + ogDescription: 'Explore powerful head management with Unhead. SSR support, social previews, and structured data out of the box.', + ogImage: 'https://unhead.unjs.io/og.png', + ogUrl: 'https://example.com/', + ogType: 'website', + ogSiteName: 'Unhead Demo', + twitterCard: 'summary_large_image', + twitterTitle: 'Unhead Demo: Head Management for Vue', + twitterDescription: 'Explore powerful head management with Unhead.', + twitterImage: 'https://unhead.unjs.io/og.png', +}) + +useSchemaOrg([ + defineWebSite({ + name: 'Unhead Demo', + url: 'https://example.com/', + inLanguage: 'en', + potentialAction: [ + defineSearchAction({ + target: { + '@type': 'EntryPoint', + urlTemplate: 'https://example.com/search?q={search_term_string}', + }, + }), + ], + }), + defineWebPage({ + '@type': 'WebPage', + name: 'Unhead Vite SSR Demo', + description: 'A demo site showcasing Unhead for Vue with SSR, featuring SEO meta tags, social sharing, and structured data.', + }), + defineOrganization({ + name: 'UnJS', + url: 'https://unjs.io', + logo: 'https://unhead.unjs.io/logo.png', + }), +]) + +// Client-only: analytics/tracking meta +if (typeof window !== 'undefined') { + useHead({ + meta: [ + { name: 'client-rendered', content: 'true' }, + ], + }) +} + +// Server-only: bot hints +if (typeof window === 'undefined') { + useHead({ + meta: [ + { name: 'server-timing', content: 'ssr;desc="Server Rendered"' }, + ], + }) +} +</script> + +<template> + <div> + <h1>Unhead Vite SSR Demo</h1> + <p>This page demonstrates social meta tags and SERP preview in devtools.</p> + + <div class="card"> + <button type="button" @click="count++">count is {{ count }}</button> + <p> + Edit + <code>pages/HomePage.vue</code> to test HMR + </p> + </div> + </div> +</template> + +<style scoped> +.card { + padding: 2rem; +} +</style> diff --git a/examples/vite-ssr-vue/src/pages/ScriptsPage.vue b/examples/vite-ssr-vue/src/pages/ScriptsPage.vue new file mode 100644 index 000000000..c53e8dcfe --- /dev/null +++ b/examples/vite-ssr-vue/src/pages/ScriptsPage.vue @@ -0,0 +1,52 @@ +<script setup lang="ts"> +useHead({ + title: 'Scripts Demo', +}) + +useScript('https://www.googletagmanager.com/gtag/js?id=G-DEMO123456', { + trigger: 'client', +}) + +const { $script: confetti } = useScript<{ confetti: (opts: any) => void }>({ + src: 'https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/dist/confetti.browser.min.js', +}, { + trigger: 'manual', + use: () => ({ confetti: window.confetti }), +}) + +function fireConfetti() { + confetti.load().then(() => { + window.confetti({ + particleCount: 150, + spread: 80, + origin: { y: 0.6 }, + }) + }) +} +</script> + +<template> + <div> + <h1>Scripts Demo</h1> + <p>This page demonstrates third-party script loading with <code>useScript</code>.</p> + + <div class="card"> + <h2>Google Analytics</h2> + <p>Loaded via <code>useScript</code> on client hydration. Check the network tab to verify.</p> + </div> + + <div class="card"> + <h2>Confetti</h2> + <p>Loaded manually on demand.</p> + <button type="button" @click="fireConfetti"> + Fire Confetti šŸŽ‰ + </button> + </div> + </div> +</template> + +<style scoped> +.card { + padding: 2rem; +} +</style> diff --git a/examples/vite-ssr-vue/src/router.ts b/examples/vite-ssr-vue/src/router.ts new file mode 100644 index 000000000..a3ac78764 --- /dev/null +++ b/examples/vite-ssr-vue/src/router.ts @@ -0,0 +1,33 @@ +import { + createRouter as _createRouter, + createMemoryHistory, + createWebHistory, +} from 'vue-router' + +const routes = [ + { + path: '/', + component: () => import('./pages/HomePage.vue'), + }, + { + path: '/about', + component: () => import('./pages/AboutPage.vue'), + }, + { + path: '/blog', + component: () => import('./pages/BlogPage.vue'), + }, + { + path: '/scripts', + component: () => import('./pages/ScriptsPage.vue'), + }, +] + +export function createRouter() { + return _createRouter({ + history: import.meta.env.SSR + ? createMemoryHistory() + : createWebHistory(), + routes, + }) +} diff --git a/examples/vite-ssr-vue/vite.config.ts b/examples/vite-ssr-vue/vite.config.ts index be990b0b5..fb78ad241 100644 --- a/examples/vite-ssr-vue/vite.config.ts +++ b/examples/vite-ssr-vue/vite.config.ts @@ -2,20 +2,32 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { DevTools } from '@vitejs/devtools' import { unheadVueComposablesImports } from '@unhead/vue' +import { schemaAutoImports } from '@unhead/schema-org' import AutoImport from 'unplugin-auto-import/vite' -import { unheadDevtools } from '@unhead/devtools/vite' +import { Unhead } from '@unhead/vue/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [ - DevTools({ clientAuth: false }), + DevTools(), AutoImport({ imports: [ unheadVueComposablesImports, 'vue', + 'vue-router', + { '@unhead/vue': ['useScript'] }, + { '@unhead/schema-org/vue': schemaAutoImports }, ], }), vue(), - unheadDevtools(), + Unhead(), ], + optimizeDeps: { + include: ['vue-router'], + }, + devtools: { + enabled: true, + clientAuth: false, + clientAuthTokens: ['123'], + } }) diff --git a/packages-aliased/addons/src/vite.ts b/packages-aliased/addons/src/vite.ts index 3fc5d7889..d43fbefaf 100644 --- a/packages-aliased/addons/src/vite.ts +++ b/packages-aliased/addons/src/vite.ts @@ -1,4 +1,4 @@ console.warn('[unhead] `@unhead/addons/vite` is deprecated. Please migrate to `@unhead/bundler/vite` instead.') -export { default } from '@unhead/bundler/vite' -export * from '@unhead/bundler/vite' +export { Unhead as default } from '@unhead/bundler/vite' +export type * from '@unhead/bundler/vite' diff --git a/packages/bundler/README.md b/packages/bundler/README.md index a94922d42..8fa063f6c 100644 --- a/packages/bundler/README.md +++ b/packages/bundler/README.md @@ -31,13 +31,13 @@ pnpm add @unhead/bundler ### Vite Plugin ```ts -import UnheadVite from '@unhead/bundler/vite' // vite.config.ts import { defineConfig } from 'vite' +import { Unhead } from '@unhead/bundler/vite' export default defineConfig({ plugins: [ - UnheadVite({ + Unhead({ // Options }) ] @@ -48,11 +48,11 @@ export default defineConfig({ ```js // webpack.config.js -const UnheadWebpack = require('@unhead/bundler/webpack') +const { Unhead } = require('@unhead/bundler/webpack') module.exports = { plugins: [ - UnheadWebpack({ + Unhead({ // Options }) ] diff --git a/packages/bundler/build.config.ts b/packages/bundler/build.config.ts index 5d73f2434..ed7076187 100644 --- a/packages/bundler/build.config.ts +++ b/packages/bundler/build.config.ts @@ -10,6 +10,7 @@ export default defineBuildConfig({ { input: 'src/minify/rolldown', name: 'minify/rolldown' }, { input: 'src/minify/esbuild', name: 'minify/esbuild' }, { input: 'src/minify/lightningcss', name: 'minify/lightningcss' }, + { input: 'src/devtools/bridge', name: 'devtools/bridge' }, ], externals: [ 'vite', @@ -23,5 +24,8 @@ export default defineBuildConfig({ 'esbuild', 'lightningcss', 'rolldown', + '@vitejs/devtools-kit', + '@vitejs/devtools-kit/client', + '@unhead/devtools-app', ], }) diff --git a/packages/bundler/package.json b/packages/bundler/package.json index a0120d441..5e906cd6e 100644 --- a/packages/bundler/package.json +++ b/packages/bundler/package.json @@ -97,6 +97,7 @@ }, "dependencies": { "@rollup/pluginutils": "catalog:", + "@unhead/devtools-app": "workspace:*", "magic-string": "catalog:", "oxc-parser": "catalog:", "oxc-walker": "catalog:", @@ -104,6 +105,7 @@ "unplugin": "catalog:" }, "devDependencies": { + "@vitejs/devtools-kit": "catalog:", "rollup": "catalog:", "unhead": "workspace:*", "vite": "catalog:" diff --git a/packages/bundler/src/devtools/bridge.ts b/packages/bundler/src/devtools/bridge.ts new file mode 100644 index 000000000..16d710a42 --- /dev/null +++ b/packages/bundler/src/devtools/bridge.ts @@ -0,0 +1,388 @@ +import type { SeoOverview, SerializedScript, SerializedTag, SerializedValidationRule, TagRenderMode, UnheadDevtoolsState } from './rpc/types' + +const __UNHEAD_VERSION__ = '' // replaced at serve-time by the Vite plugin + +declare global { + interface Window { + __unhead__?: { + _head?: any + _q?: any[] + push?: (e: any) => void + } + __unhead_devtools__?: any + } +} + +function extractSeoOverview(tags: SerializedTag[], title: string): SeoOverview { + const seo: SeoOverview = { + title, + description: '', + canonical: '', + robots: '', + ogTitle: '', + ogDescription: '', + ogImage: '', + } + for (const t of tags) { + if (t.tag === 'meta') { + const name = t.props.name + const property = t.props.property + const content = t.props.content || '' + if (name === 'description') + seo.description = content + else if (name === 'robots') + seo.robots = content + else if (property === 'og:title') + seo.ogTitle = content + else if (property === 'og:description') + seo.ogDescription = content + else if (property === 'og:image') + seo.ogImage = content + } + else if (t.tag === 'link' && t.props.rel === 'canonical') { + seo.canonical = t.props.href || '' + } + } + return seo +} + +/** + * Serializes a value preserving type information for non-serializable types. + * Functions become "ʒ functionName()", null stays null, undefined becomes "⊘ undefined". + */ +function safeSerialize(value: any, seen = new WeakSet()): any { + if (value === null) + return null + if (value === undefined) + return '⊘ undefined' + if (typeof value === 'function') + return `ʒ ${value.name || 'anonymous'}()` + if (typeof value === 'symbol') + return `Symbol(${value.description || ''})` + if (typeof value !== 'object') + return value + if (seen.has(value)) + return '⊘ circular' + seen.add(value) + if (Array.isArray(value)) + return value.map(v => safeSerialize(v, seen)) + const result: Record<string, any> = {} + for (const key in value) { + if (key === '__proto__' || key === 'constructor' || key === 'prototype') + continue + result[key] = safeSerialize(value[key], seen) + } + return result +} + +function resolveEntryMode(entry: any, wasSSR: boolean, ssrSources: Set<string>): TagRenderMode { + if (entry._devtoolsMode) + return entry._devtoolsMode + if (!wasSSR) + return 'client' + // Match against SSR payload sources to determine if this entry was server-rendered + const source = entry.options?._source + if (source && ssrSources.has(source)) + return 'hydrated' + return 'client' +} + +function readSSRDevtoolsPayload(): { entries: any[], tags?: any[] } | null { + const el = document.head.querySelector('script[id="unhead:devtools"]') + if (!el?.innerHTML) + return null + try { + return JSON.parse(el.innerHTML) + } + catch { + return null + } +} + +function serializeHeadState(head: any, wasSSR = false, ssrPayload: { entries: any[], tags?: any[] } | null = null): UnheadDevtoolsState { + const entries: UnheadDevtoolsState['entries'] = [] + const allTags: UnheadDevtoolsState['tags'] = [] + const tagTypeCounts: Record<string, number> = {} + const weightFn = head.resolvedOptions?._tagWeight || ((tag: any) => typeof tag.tagPriority === 'number' ? tag.tagPriority : 100) + + // Build set of sources that existed on the server + const ssrSources = new Set<string>() + if (ssrPayload?.entries) { + for (const e of ssrPayload.entries) { + if (e.source) + ssrSources.add(e.source) + } + } + + // Collect client entry IDs to find server-only entries + const clientEntryIds = new Set<number>() + + if (head.entries) { + for (const [id, entry] of head.entries) { + clientEntryIds.add(id) + const tags = entry._tags || [] + const mode = resolveEntryMode(entry, wasSSR, ssrSources) + entries.push({ + id, + source: entry.options?._source, + input: safeSerialize(entry.input || {}), + tagCount: tags.length, + mode, + }) + for (const tag of tags) { + const tagName = tag.tag + tagTypeCounts[tagName] = (tagTypeCounts[tagName] || 0) + 1 + const weight = tag._w ?? weightFn(tag) + allTags.push({ + tag: tagName, + props: { ...tag.props }, + innerHTML: tag.innerHTML, + textContent: tag.textContent, + position: tag.tagPosition, + priority: weight, + order: tag._p, + dedupeKey: tag._d, + source: tag._source || entry.options?._source, + mode, + }) + } + } + } + + // Patch JSON-LD tags with resolved innerHTML from the DOM. + // The schema-org plugin sets innerHTML during tags:resolve which operates + // on copies, so entry._tags never receives it. + for (const el of document.querySelectorAll('script[type="application/ld+json"]')) { + if (el.id === 'unhead:devtools' || el.id === 'unhead:payload') + continue + const innerHTML = el.innerHTML + if (!innerHTML) + continue + // Find the matching serialized tag and patch it + const dedupeKey = el.getAttribute('data-hid') || '' + const matched = allTags.find( + t => t.tag === 'script' && t.props.type === 'application/ld+json' && !t.innerHTML + && (dedupeKey ? (t.dedupeKey?.includes(dedupeKey) || t.props['data-hid'] === dedupeKey) : true), + ) + if (matched) { + matched.innerHTML = innerHTML + } + else { + // DOM has a JSON-LD script not in entry._tags (e.g. fully plugin-managed) + allTags.push({ + tag: 'script', + props: { type: 'application/ld+json', ...(dedupeKey ? { 'data-hid': dedupeKey } : {}) }, + innerHTML, + mode: 'client' as TagRenderMode, + } as any) + tagTypeCounts.script = (tagTypeCounts.script || 0) + 1 + } + } + + // Merge server-only data from SSR payload + if (ssrPayload) { + // Server-only entries + if (ssrPayload.entries) { + for (const ssrEntry of ssrPayload.entries) { + if (!clientEntryIds.has(ssrEntry.id)) { + entries.push({ + id: ssrEntry.id, + source: ssrEntry.source, + input: ssrEntry.input || {}, + tagCount: ssrEntry.tagCount, + mode: 'server' as TagRenderMode, + }) + } + } + } + // Server-only tags (not present in client) + if (ssrPayload.tags) { + const tagFingerprint = (t: any) => t.dedupeKey || `${t.tag}:${JSON.stringify(t.props || {})}` + const clientTagKeys = new Set(allTags.map(t => tagFingerprint(t))) + for (const ssrTag of ssrPayload.tags) { + if (clientTagKeys.has(tagFingerprint(ssrTag))) + continue + const tagName = ssrTag.tag + tagTypeCounts[tagName] = (tagTypeCounts[tagName] || 0) + 1 + allTags.push({ + tag: tagName, + props: ssrTag.props || {}, + innerHTML: ssrTag.innerHTML, + textContent: ssrTag.textContent, + position: ssrTag.position, + priority: ssrTag.priority, + dedupeKey: ssrTag.dedupeKey, + source: ssrTag.source, + mode: 'server' as TagRenderMode, + }) + } + } + } + + const plugins: string[] = [] + if (head.plugins) { + for (const [key] of head.plugins) { + plugins.push(key) + } + } + + const scripts: SerializedScript[] = [] + if (head._scripts) { + for (const [id, script] of Object.entries(head._scripts)) { + const s = script as any + scripts.push({ + id, + src: s.src || s.input?.src || '', + status: s.status || 'unknown', + warmupStrategy: s._warmupEl ? (s._warmupStrategy || 'preload') : undefined, + events: s._events || [], + fetchpriority: s.input?.fetchpriority, + crossorigin: s.input?.crossorigin, + defer: s.input?.defer, + async: s.input?.async, + }) + } + } + + let templateParams: Record<string, any> | null = null + if (head._templateParams) { + try { + templateParams = JSON.parse(JSON.stringify(head._templateParams)) + } + catch {} + } + + const title = head._title || document.title || '' + + // Read validation rules stored by ValidatePlugin (if active) + const validationRules: SerializedValidationRule[] = (head._validationRules || []).map((r: any) => ({ + id: r.id, + message: r.message, + severity: r.severity, + source: r.source, + tagDedupeKey: r.tag?._d || r.tag?._h || undefined, + })) + + return { + version: __UNHEAD_VERSION__, + entries, + tags: allTags.sort((a, b) => (a.priority ?? 100) === (b.priority ?? 100) ? (a.order ?? 0) - (b.order ?? 0) : (a.priority ?? 100) - (b.priority ?? 100)), + plugins, + title, + scripts, + seo: extractSeoOverview(allTags, title), + titleTemplate: head._titleTemplate + ? (typeof head._titleTemplate === 'function' ? String(head._titleTemplate) : head._titleTemplate) + : null, + templateParams, + separator: head._separator || '|', + ssr: !!head.ssr, + dirty: !!head.dirty, + domElementCount: head._dom?._e?.size || 0, + tagTypeCounts, + validationRules, + } +} + +function detectSSR(): boolean { + // Check if the page has server-rendered head elements (meta/link with content) + const headEl = document.head + return !!( + headEl.querySelector('meta[name="description"]') + || headEl.querySelector('meta[property]') + || headEl.querySelector('link[rel="canonical"]') + || headEl.querySelector('script[id="unhead:payload"]') + ) +} + +function connectBridge(head: any) { + let sharedState: any + + const wasSSR = detectSSR() + const ssrPayload = readSSRDevtoolsPayload() + + function syncToSharedState() { + if (!sharedState) + return + const newState = serializeHeadState(head, wasSSR, ssrPayload) + sharedState.mutate((draft: any) => { + Object.assign(draft, newState) + }) + } + + async function init() { + const { getDevToolsClientContext } = await import('@vitejs/devtools-kit/client') + + // Retry until DevTools client context is available + let ctx = getDevToolsClientContext() + if (!ctx) { + let retries = 0 + await new Promise<void>((resolve) => { + const timer = globalThis.setInterval(() => { + ctx = getDevToolsClientContext() + if (ctx || ++retries > 50) { + globalThis.clearInterval(timer) + if (!ctx) + console.warn('[unhead bridge] gave up waiting for DevTools context after 50 retries') + resolve() + } + }, 100) + }) + } + if (!ctx) { + console.warn('[unhead bridge] no DevTools client context, aborting') + return + } + + sharedState = await (ctx.rpc.sharedState as any).get('unhead:state', { + initialValue: serializeHeadState(head, wasSSR, ssrPayload), + }) + + if (head.hooks) + head.hooks.hook('dom:rendered', syncToSharedState) + + // Initial sync after a short delay to capture early entries + setTimeout(syncToSharedState, 500) + } + + init().catch((err) => { + console.error('[unhead bridge] init failed:', err) + }) +} + +function findHead(): any { + // 1. Streaming plugin pattern: window.__unhead__._head + if (window.__unhead__?._head) + return window.__unhead__._head + + // 2. Devtools-exposed pattern: window.__unhead_devtools__ + if (window.__unhead_devtools__) + return window.__unhead_devtools__ + + return null +} + +function pollForHead() { + let attempts = 0 + const handle = globalThis.setInterval(() => { + const h = findHead() + if (h) { + globalThis.clearInterval(handle) + connectBridge(h) + } + if (++attempts > 50) { + globalThis.clearInterval(handle) + console.warn('[unhead bridge] gave up polling for head after 50 attempts') + } + }, 100) +} + +if (typeof window !== 'undefined') { + const head = findHead() + if (head) { + connectBridge(head) + } + else { + pollForHead() + } +} diff --git a/packages/bundler/src/devtools/plugin.ts b/packages/bundler/src/devtools/plugin.ts new file mode 100644 index 000000000..ab7e4b097 --- /dev/null +++ b/packages/bundler/src/devtools/plugin.ts @@ -0,0 +1,75 @@ +import type { HeadPluginInput } from 'unhead/types' + +/** + * Unhead plugin that propagates source location metadata from entry options to tags. + * + * On the server, it also serializes the full devtools state into a + * `<script id="unhead:devtools">` payload so the client bridge can display + * server-only entries. + */ +export function devtoolsPlugin(): HeadPluginInput { + return (head: any) => { + return { + key: 'devtools', + hooks: { + 'entries:normalize': function ({ tags, entry }: { tags: any[], entry: any }) { + const source = entry.options?._source + if (!source) + return + for (const tag of tags) { + if (!tag._source) + tag._source = source + } + }, + 'tags:resolve': function (ctx: any) { + if (!head.ssr) + return + // Serialize SSR entries into a payload for the client bridge + const entries: any[] = [] + for (const [id, entry] of head.entries) { + let input: Record<string, any> = {} + try { + input = JSON.parse(JSON.stringify(entry.input || {}, (_k, v) => { + if (typeof v === 'function') + return `ʒ ${v.name || 'anonymous'}()` + if (typeof v === 'undefined') + return '⊘ undefined' + return v + })) + } + catch {} + entries.push({ + id, + source: entry.options?._source, + input, + tagCount: (entry._tags || []).length, + mode: 'server', + }) + } + // Serialize resolved tags (exclude the devtools payload script itself) + const tags: any[] = [] + for (const tag of ctx.tags) { + if (tag.props?.id === 'unhead:devtools' || tag.props?.id === 'unhead:payload') + continue + tags.push({ + tag: tag.tag, + props: { ...tag.props }, + innerHTML: tag.innerHTML, + textContent: tag.textContent, + position: tag.tagPosition, + priority: tag._w, + dedupeKey: tag._d, + source: tag._source, + mode: 'server', + }) + } + ctx.tags.push({ + tag: 'script', + innerHTML: JSON.stringify({ entries, tags }), + props: { id: 'unhead:devtools', type: 'application/json' }, + }) + }, + }, + } + } +} diff --git a/packages/devtools/src/rpc/functions/get-config.ts b/packages/bundler/src/devtools/rpc/functions/get-config.ts similarity index 100% rename from packages/devtools/src/rpc/functions/get-config.ts rename to packages/bundler/src/devtools/rpc/functions/get-config.ts diff --git a/packages/devtools/src/rpc/index.ts b/packages/bundler/src/devtools/rpc/index.ts similarity index 100% rename from packages/devtools/src/rpc/index.ts rename to packages/bundler/src/devtools/rpc/index.ts diff --git a/packages/devtools/src/rpc/types.ts b/packages/bundler/src/devtools/rpc/types.ts similarity index 77% rename from packages/devtools/src/rpc/types.ts rename to packages/bundler/src/devtools/rpc/types.ts index 3e62d707d..b484fe171 100644 --- a/packages/devtools/src/rpc/types.ts +++ b/packages/bundler/src/devtools/rpc/types.ts @@ -1,8 +1,11 @@ +export type TagRenderMode = 'server' | 'client' | 'hydrated' + export interface SerializedEntry { id: number source?: string input: Record<string, any> tagCount: number + mode: TagRenderMode } export interface SerializedTag { @@ -12,14 +15,22 @@ export interface SerializedTag { textContent?: string position?: string priority?: number + order?: number dedupeKey?: string source?: string + mode: TagRenderMode } export interface SerializedScript { id: string src: string status: string + warmupStrategy?: string + events: { type: string, timestamp: number }[] + fetchpriority?: string + crossorigin?: string + defer?: boolean + async?: boolean } export interface SeoOverview { @@ -37,9 +48,11 @@ export interface SerializedValidationRule { message: string severity: 'warn' | 'info' source?: string + tagDedupeKey?: string } export interface UnheadDevtoolsState { + version: string entries: SerializedEntry[] tags: SerializedTag[] plugins: string[] diff --git a/packages/devtools/src/vite.ts b/packages/bundler/src/devtools/vite.ts similarity index 60% rename from packages/devtools/src/vite.ts rename to packages/bundler/src/devtools/vite.ts index 8a54ab3e0..2e84a033a 100644 --- a/packages/devtools/src/vite.ts +++ b/packages/bundler/src/devtools/vite.ts @@ -1,5 +1,7 @@ /// <reference types="@vitejs/devtools-kit" /> import type { Plugin } from 'vite' +import type { HeadTransformContext } from '../unplugin/CreateHeadTransform' +import type { UnheadDevtoolsOptions } from '../unplugin/types' import { existsSync, readFileSync } from 'node:fs' import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' @@ -8,29 +10,25 @@ import { parseAndWalk } from 'oxc-walker' import { getConfigRpc } from './rpc' const HEAD_COMPOSABLES = ['useHead', 'useSeoMeta', 'useHeadSafe', 'useScript'] -const HEAD_FACTORIES = ['createHead'] const FILE_RE = /\.(vue|tsx?|jsx?|svelte)$/ +const LEADING_SLASH_RE = /^\// -const UNHEAD_ICON = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%2300dc82' d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39'/%3E%3C/svg%3E` +const UNHEAD_ICON = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%23FBBF24'/%3E%3Cstop offset='100%25' stop-color='%23f0db4f'/%3E%3C/linearGradient%3E%3Cmask id='m'%3E%3Crect width='100%25' height='100%25' fill='white'/%3E%3Cpath d='M12 32 L1 32 L15 15 Z' fill='black'/%3E%3C/mask%3E%3C/defs%3E%3Cpath fill='none' stroke='url(%23g)' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 4v14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4' mask='url(%23m)'/%3E%3C/svg%3E` const DEVTOOLS_UI_ROUTE = '/__unhead/' -function hasHeadCode(code: string): boolean { - return HEAD_COMPOSABLES.some(c => code.includes(c)) || HEAD_FACTORIES.some(c => code.includes(c)) -} - /** * Transforms source code to inject `_source` metadata into head composable calls. */ function transformSourceLocations(code: string, id: string, root: string): { code: string, map: any } | undefined { - if (!hasHeadCode(code)) + if (!HEAD_COMPOSABLES.some(c => code.includes(c))) return const s = new MagicString(code) let transformed = false const relativePath = id.startsWith(root) - ? id.slice(root.length).replace(/^\//, '') + ? id.slice(root.length).replace(LEADING_SLASH_RE, '') : id parseAndWalk(code, id, { @@ -48,14 +46,6 @@ function transformSourceLocations(code: string, id: string, root: string): { cod ? callee.property.name : null - // Detect createHead() and wrap to expose on window for the bridge - if (name && HEAD_FACTORIES.includes(name)) { - s.prependLeft(node.start, `((_h)=>(typeof window!=='undefined'&&(window.__unhead_devtools__=_h),_h))(`) - s.appendRight(node.end, `)`) - transformed = true - return - } - if (!name || !HEAD_COMPOSABLES.includes(name)) return @@ -88,9 +78,15 @@ function transformSourceLocations(code: string, id: string, root: string): { cod } } -export function unheadDevtools(): Plugin { +export interface UnheadDevtoolsInternalOptions extends UnheadDevtoolsOptions { + _ctx?: HeadTransformContext +} + +export function unheadDevtools(options?: UnheadDevtoolsInternalOptions): Plugin { let root = '' + let enabled = false let bridgeCode: string | undefined + let unheadVersion = '' const pkgDir = fileURLToPath(new URL('..', import.meta.url)) return { @@ -99,12 +95,35 @@ export function unheadDevtools(): Plugin { configResolved(config) { root = config.root - const bridgePath = resolve(pkgDir, 'dist/bridge.mjs') + enabled = config.plugins.some(p => p.name?.startsWith('vite:devtools')) + if (!enabled) + return + + // Register runtime plugins via the shared context + if (options?._ctx) { + options._ctx.addRuntimePlugin({ + import: { name: 'devtoolsPlugin', source: '@unhead/bundler', as: '__unhead_devtoolsPlugin' }, + client: 'window.__unhead_devtools__=_h', + server: '_h.use(__unhead_devtoolsPlugin())', + }) + } + + // Resolve unhead version for the devtools UI + try { + const unheadPkg = resolve(pkgDir, 'node_modules/unhead/package.json') + if (existsSync(unheadPkg)) + unheadVersion = JSON.parse(readFileSync(unheadPkg, 'utf-8')).version || '' + } + catch {} + + const bridgePath = resolve(pkgDir, 'dist/devtools/bridge.mjs') if (existsSync(bridgePath)) bridgeCode = readFileSync(bridgePath, 'utf-8') }, configureServer(server) { + if (!enabled) + return // Bridge middleware server.middlewares.use('/@unhead/bridge.mjs', async (_req, res) => { const result = await server.transformRequest('/@unhead/bridge.mjs') @@ -114,40 +133,51 @@ export function unheadDevtools(): Plugin { }, resolveId(id) { + if (!enabled) + return if (id === '/@unhead/bridge.mjs') return id }, load(id) { - if (id !== '/@unhead/bridge.mjs') + if (!enabled || id !== '/@unhead/bridge.mjs') return if (!bridgeCode) return 'console.warn("[unhead devtools] bridge not built")' + let code = bridgeCode + // Inject unhead version + if (unheadVersion) + code = code.replace(`__UNHEAD_VERSION__ = ''`, `__UNHEAD_VERSION__ = '${unheadVersion}'`) const kitClientPath = resolve(pkgDir, 'node_modules/@vitejs/devtools-kit/dist/client.js') if (existsSync(kitClientPath)) - return bridgeCode.replace(`'@vitejs/devtools-kit/client'`, `'${kitClientPath}'`) - return bridgeCode + return code.replace(`'@vitejs/devtools-kit/client'`, `'${kitClientPath}'`) + return code }, transform: { filter: { id: FILE_RE }, handler(code, id) { + if (!enabled) + return return transformSourceLocations(code, id, root) }, }, transformIndexHtml() { + if (!enabled) + return [] return [{ tag: 'script', attrs: { type: 'module' }, children: `import("/@unhead/bridge.mjs")`, - injectTo: 'body-close', + injectTo: 'body', }] }, devtools: { setup(ctx) { - const clientPath = resolve(pkgDir, '../devtools-app/dist') + // devtools-app dist is in a sibling package + const clientPath = resolve(pkgDir, 'node_modules/@unhead/devtools-app/dist') if (existsSync(clientPath)) { ctx.views.hostStatic(DEVTOOLS_UI_ROUTE, clientPath) } diff --git a/packages/bundler/src/index.ts b/packages/bundler/src/index.ts index 014378dca..d8cacf62b 100644 --- a/packages/bundler/src/index.ts +++ b/packages/bundler/src/index.ts @@ -1,3 +1,4 @@ export * from './constants' +export { devtoolsPlugin } from './devtools/plugin' export { InferSeoMetaPlugin } from 'unhead/plugins' diff --git a/packages/bundler/src/unplugin/CreateHeadTransform.ts b/packages/bundler/src/unplugin/CreateHeadTransform.ts new file mode 100644 index 000000000..cc061547d --- /dev/null +++ b/packages/bundler/src/unplugin/CreateHeadTransform.ts @@ -0,0 +1,104 @@ +import type { Plugin } from 'vite' +import MagicString from 'magic-string' +import { parseAndWalk } from 'oxc-walker' + +const HEAD_FACTORIES = ['createHead'] +const FILE_RE = /\.(vue|tsx?|jsx?|svelte)$/ + +export interface RuntimePluginRegistration { + /** Import to inject */ + import: { name: string, source: string, as: string } + /** Expression using `_h` (the head instance). Only emitted in client builds. */ + client?: string + /** Expression using `_h` (the head instance). Only emitted in server builds. */ + server?: string +} + +export interface HeadTransformContext { + addRuntimePlugin: (reg: RuntimePluginRegistration) => void + getRegistrations: () => RuntimePluginRegistration[] +} + +export function createHeadTransformContext(): HeadTransformContext { + const registrations: RuntimePluginRegistration[] = [] + return { + addRuntimePlugin(reg) { + registrations.push(reg) + }, + getRegistrations() { + return registrations + }, + } +} + +export function CreateHeadTransform(ctx: HeadTransformContext): Plugin { + let root = '' + + return { + name: '@unhead/create-head-transform', + apply: 'serve', + + configResolved(config) { + root = config.root + }, + + transform: { + filter: { id: FILE_RE }, + handler(code, id) { + const registrations = ctx.getRegistrations() + if (!registrations.length) + return + if (!HEAD_FACTORIES.some(f => code.includes(f))) + return + + const isServer = this.environment?.config?.consumer === 'server' + const envRegistrations = registrations.filter(r => isServer ? r.server : r.client) + if (!envRegistrations.length) + return + + const s = new MagicString(code) + let transformed = false + + parseAndWalk(code, id, { + parseOptions: { lang: 'ts' }, + enter(node: any) { + if (node.type !== 'CallExpression') + return + const callee = node.callee + if (!callee) + return + + const name = callee.type === 'Identifier' + ? callee.name + : callee.type === 'MemberExpression' && callee.property?.type === 'Identifier' + ? callee.property.name + : null + + if (!name || !HEAD_FACTORIES.includes(name)) + return + + const statements = envRegistrations + .map(r => (isServer ? r.server! : r.client!).replace(/__ROOT__/g, JSON.stringify(root))) + .join(',') + + s.prependLeft(node.start, `((_h)=>(${statements},_h))(`) + s.appendRight(node.end, `)`) + transformed = true + }, + }) + + if (!transformed) + return + + for (const reg of envRegistrations) { + s.prepend(`import { ${reg.import.name} as ${reg.import.as} } from '${reg.import.source}';\n`) + } + + return { + code: s.toString(), + map: s.generateMap({ includeContent: true, source: id }), + } + }, + }, + } +} diff --git a/packages/bundler/src/unplugin/SSRStaticReplace.ts b/packages/bundler/src/unplugin/SSRStaticReplace.ts new file mode 100644 index 000000000..ea1030f83 --- /dev/null +++ b/packages/bundler/src/unplugin/SSRStaticReplace.ts @@ -0,0 +1,51 @@ +import type { ConfigEnv, UserConfig } from 'vite' +import MagicString from 'magic-string' +import { createUnplugin } from 'unplugin' + +const UNHEAD_MODULE_RE = /[\\/]node_modules[\\/](?:@unhead[\\/][^\\/]+|unhead)[\\/]/ +const HEAD_SSR_RE = /\bhead\.ssr\b/g +const JS_RE = /\.(?:c|m)?js$/ + +export const SSRStaticReplace = createUnplugin<Record<string, never>, false>(() => { + let ssr = false + + return { + name: 'unhead:ssr-static-replace', + enforce: 'pre', + + transformInclude(id) { + if (!UNHEAD_MODULE_RE.test(id)) + return false + return JS_RE.test(id) + }, + + transform(code) { + if (!code.includes('head.ssr')) + return + + const s = new MagicString(code) + for (const match of code.matchAll(HEAD_SSR_RE)) { + s.overwrite(match.index!, match.index! + match[0].length, String(ssr)) + } + + if (s.hasChanged()) { + return { + code: s.toString(), + map: s.generateMap({ includeContent: true }), + } + } + }, + + webpack(ctx) { + if (ctx.name === 'server') + ssr = true + }, + vite: { + apply(_config: UserConfig, env: ConfigEnv) { + if (env.isSsrBuild) + ssr = true + return false + }, + }, + } +}) diff --git a/packages/bundler/src/unplugin/UseSeoMetaTransform.ts b/packages/bundler/src/unplugin/UseSeoMetaTransform.ts index 99a2e8d58..b003f06a3 100644 --- a/packages/bundler/src/unplugin/UseSeoMetaTransform.ts +++ b/packages/bundler/src/unplugin/UseSeoMetaTransform.ts @@ -159,8 +159,13 @@ export const UseSeoMetaTransform = createUnplugin<UseSeoMetaTransformOptions, fa } } if (originalName === 'useServerSeoMeta') { - if (output.length) - output.push('});') + if (output.length) { + const secondArg = node.arguments[1] + if (secondArg) + output.push(`}, ${code.substring(secondArg.start, secondArg.end)});`) + else + output.push('});') + } output.push('useServerHead({') } @@ -233,7 +238,14 @@ export const UseSeoMetaTransform = createUnplugin<UseSeoMetaTransformOptions, fa if (output) { if (meta.length) output.push(' ]') - output.push('})') + // Preserve the second argument (options like { head }) if present + if (node.arguments.length >= 2) { + const optionsArg = code.substring(node.arguments[1].start, node.arguments[1].end) + output.push(`}, ${optionsArg})`) + } + else { + output.push('})') + } s.overwrite(node.start, node.end, output.join('\n')) // Track import for rewriting diff --git a/packages/bundler/src/unplugin/types.ts b/packages/bundler/src/unplugin/types.ts index d83b46f4e..793830fcb 100644 --- a/packages/bundler/src/unplugin/types.ts +++ b/packages/bundler/src/unplugin/types.ts @@ -15,3 +15,14 @@ export interface UnpluginOptions extends BaseTransformerTypes { transformSeoMeta?: UseSeoMetaTransformOptions | false minify?: MinifyTransformOptions | false } + +export interface VitePluginOptions extends UnpluginOptions { + /** Vite DevTools integration (dev-only). Enabled by default, set `false` to disable. */ + devtools?: UnheadDevtoolsOptions | false + /** Inject ValidatePlugin in dev to surface head tag warnings in the console. Enabled by default, set `false` to disable. */ + validate?: boolean + /** @internal */ + _framework?: string +} + +export interface UnheadDevtoolsOptions {} diff --git a/packages/bundler/src/unplugin/vite.ts b/packages/bundler/src/unplugin/vite.ts index 1884209a6..25ac196cd 100644 --- a/packages/bundler/src/unplugin/vite.ts +++ b/packages/bundler/src/unplugin/vite.ts @@ -1,13 +1,19 @@ import type { Plugin } from 'vite' -import type { UnpluginOptions } from './types' +import type { VitePluginOptions } from './types' +import { unheadDevtools } from '../devtools/vite' +import { CreateHeadTransform, createHeadTransformContext } from './CreateHeadTransform' import { MinifyTransform } from './MinifyTransform' +import { SSRStaticReplace } from './SSRStaticReplace' import { TreeshakeServerComposables } from './TreeshakeServerComposables' import { UseSeoMetaTransform } from './UseSeoMetaTransform' -export type { UnpluginOptions } +export type { VitePluginOptions } -export default (options: UnpluginOptions = {}): Plugin[] => { +export function Unhead(options: VitePluginOptions = {}): Plugin[] { const plugins: Plugin[] = [] + const ctx = createHeadTransformContext() + const framework = options._framework + if (options.treeshake !== false) { const treeshakeOpts = typeof options.treeshake === 'object' ? options.treeshake : {} plugins.push(TreeshakeServerComposables.vite({ filter: options.filter, sourcemap: options.sourcemap, ...treeshakeOpts })) @@ -22,5 +28,27 @@ export default (options: UnpluginOptions = {}): Plugin[] => { plugins.push(MinifyTransform.vite({ filter: options.filter, sourcemap: options.sourcemap, ...minifyOpts })) } } + + // Register runtime plugins into the shared context + if (options.validate !== false) { + const pluginsSource = framework ? `${framework}/plugins` : 'unhead/plugins' + ctx.addRuntimePlugin({ + import: { name: 'ValidatePlugin', source: pluginsSource, as: '__unhead_validate' }, + client: '_h.use(__unhead_validate({ root: __ROOT__ }))', + }) + } + + // Devtools registers its own runtime plugins via ctx during configResolved + if (options.devtools !== false) { + const devtoolsOpts = typeof options.devtools === 'object' ? options.devtools : {} + plugins.push(unheadDevtools({ ...devtoolsOpts, _ctx: ctx })) + } + + // Replace head.ssr with static boolean for tree-shaking + plugins.push(SSRStaticReplace.vite({})) + + // Single transform handles all createHead() wrapping + plugins.push(CreateHeadTransform(ctx)) + return plugins } diff --git a/packages/bundler/src/unplugin/webpack.ts b/packages/bundler/src/unplugin/webpack.ts index d4e9e78f6..5f69184da 100644 --- a/packages/bundler/src/unplugin/webpack.ts +++ b/packages/bundler/src/unplugin/webpack.ts @@ -1,11 +1,12 @@ import type { UnpluginOptions } from './types' import { MinifyTransform } from './MinifyTransform' +import { SSRStaticReplace } from './SSRStaticReplace' import { TreeshakeServerComposables } from './TreeshakeServerComposables' import { UseSeoMetaTransform } from './UseSeoMetaTransform' export type { UnpluginOptions } -export default (options: UnpluginOptions = {}) => { +export function Unhead(options: UnpluginOptions = {}) { const plugins: any[] = [] if (options.treeshake !== false) { const treeshakeOpts = typeof options.treeshake === 'object' ? options.treeshake : {} @@ -21,5 +22,6 @@ export default (options: UnpluginOptions = {}) => { plugins.push(MinifyTransform.webpack({ filter: options.filter, sourcemap: options.sourcemap, ...minifyOpts })) } } + plugins.push(SSRStaticReplace.webpack({})) return plugins } diff --git a/packages/bundler/test/createHeadTransform.test.ts b/packages/bundler/test/createHeadTransform.test.ts new file mode 100644 index 000000000..1444812d0 --- /dev/null +++ b/packages/bundler/test/createHeadTransform.test.ts @@ -0,0 +1,125 @@ +import { describe, expect, it } from 'vitest' +import { CreateHeadTransform, createHeadTransformContext } from '../src/unplugin/CreateHeadTransform' + +function createPlugin(registrations: Parameters<ReturnType<typeof createHeadTransformContext>['addRuntimePlugin']>[0][], consumer: 'client' | 'server' = 'client') { + const ctx = createHeadTransformContext() + for (const reg of registrations) + ctx.addRuntimePlugin(reg) + const plugin = CreateHeadTransform(ctx) as any + // Simulate configResolved + plugin.configResolved({ root: '/project' }) + const mockContext = { + environment: { config: { consumer } }, + } + return { + transform(code: string, id = 'entry.ts') { + return plugin.transform.handler.call(mockContext, code, id) + }, + } +} + +describe('createHeadTransform', () => { + it('ignores files without createHead', async () => { + const { transform } = createPlugin([{ + import: { name: 'ValidatePlugin', source: '@unhead/vue/plugins', as: '__validate' }, + client: '_h.use(__validate())', + }]) + expect(await transform('const x = 1')).toBeUndefined() + }) + + // Note: non-JS file filtering is handled by Vite's transform.filter.id at runtime, + // not by the handler itself, so we don't test it here. + + it('does nothing when no registrations exist', async () => { + const { transform } = createPlugin([]) + expect(await transform('const head = createHead()')).toBeUndefined() + }) + + it('wraps createHead with client plugin on client', async () => { + const { transform } = createPlugin([{ + import: { name: 'ValidatePlugin', source: '@unhead/vue/plugins', as: '__validate' }, + client: '_h.use(__validate({ root: __ROOT__ }))', + }], 'client') + const result = await transform(`import { createHead } from '@unhead/vue/client'\nconst head = createHead()`) + expect(result.code).toContain('import { ValidatePlugin as __validate } from \'@unhead/vue/plugins\'') + expect(result.code).toContain('_h.use(__validate({ root: "/project" }))') + expect(result.code).not.toContain('typeof window') + }) + + it('wraps createHead with server plugin on server', async () => { + const { transform } = createPlugin([{ + import: { name: 'devtoolsPlugin', source: '@unhead/bundler', as: '__devtools' }, + server: '_h.use(__devtools())', + }], 'server') + const result = await transform(`import { createHead } from '@unhead/vue/server'\nconst head = createHead()`) + expect(result.code).toContain('import { devtoolsPlugin as __devtools } from \'@unhead/bundler\'') + expect(result.code).toContain('_h.use(__devtools())') + }) + + it('skips client-only registrations on server', async () => { + const { transform } = createPlugin([{ + import: { name: 'ValidatePlugin', source: '@unhead/vue/plugins', as: '__validate' }, + client: '_h.use(__validate())', + }], 'server') + expect(await transform('const head = createHead()')).toBeUndefined() + }) + + it('skips server-only registrations on client', async () => { + const { transform } = createPlugin([{ + import: { name: 'devtoolsPlugin', source: '@unhead/bundler', as: '__devtools' }, + server: '_h.use(__devtools())', + }], 'client') + expect(await transform('const head = createHead()')).toBeUndefined() + }) + + it('combines multiple registrations', async () => { + const { transform } = createPlugin([ + { + import: { name: 'ValidatePlugin', source: '@unhead/vue/plugins', as: '__validate' }, + client: '_h.use(__validate())', + }, + { + import: { name: 'devtoolsPlugin', source: '@unhead/bundler', as: '__devtools' }, + client: 'window.__unhead_devtools__=_h', + }, + ], 'client') + const result = await transform('const head = createHead()') + expect(result.code).toContain('import { ValidatePlugin as __validate }') + expect(result.code).toContain('import { devtoolsPlugin as __devtools }') + expect(result.code).toContain('_h.use(__validate()),window.__unhead_devtools__=_h') + }) + + it('replaces __ROOT__ with project root', async () => { + const { transform } = createPlugin([{ + import: { name: 'ValidatePlugin', source: 'unhead/plugins', as: '__validate' }, + client: '_h.use(__validate({ root: __ROOT__ }))', + }], 'client') + const result = await transform('const head = createHead()') + expect(result.code).toContain('root: "/project"') + }) + + it('handles member expression calls like foo.createHead()', async () => { + const { transform } = createPlugin([{ + import: { name: 'ValidatePlugin', source: 'unhead/plugins', as: '__validate' }, + client: '_h.use(__validate())', + }], 'client') + const result = await transform('const head = unhead.createHead()') + expect(result.code).toContain('_h.use(__validate())') + }) + + it('only imports registrations relevant to the environment', async () => { + const { transform } = createPlugin([ + { + import: { name: 'ValidatePlugin', source: '@unhead/vue/plugins', as: '__validate' }, + client: '_h.use(__validate())', + }, + { + import: { name: 'devtoolsPlugin', source: '@unhead/bundler', as: '__devtools' }, + server: '_h.use(__devtools())', + }, + ], 'client') + const result = await transform('const head = createHead()') + expect(result.code).toContain('import { ValidatePlugin as __validate }') + expect(result.code).not.toContain('import { devtoolsPlugin as __devtools }') + }) +}) diff --git a/packages/bundler/test/useSeoMetaTransform.test.ts b/packages/bundler/test/useSeoMetaTransform.test.ts index ecff45e6a..19fb7e2e1 100644 --- a/packages/bundler/test/useSeoMetaTransform.test.ts +++ b/packages/bundler/test/useSeoMetaTransform.test.ts @@ -714,6 +714,33 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ `) }) + it('preserves second argument (options) when present', async () => { + const code = await transform([ + 'import { useSeoMeta } from \'@unhead/vue\'', + 'useSeoMeta({ title: \'Hello\', description: \'World\' }, { head })', + ]) + expect(code).toBeDefined() + expect(code).toContain('{ head }') + expect(code).toMatchInlineSnapshot(` + "import { useHead } from '@unhead/vue' + useHead({ + title: 'Hello', + meta: [ + { name: 'description', content: 'World' }, + ] + }, { head })" + `) + }) + + it('preserves complex second argument', async () => { + const code = await transform([ + 'import { useSeoMeta } from \'unhead\'', + 'useSeoMeta({ description: \'Test\' }, { head, mode: \'server\' })', + ]) + expect(code).toBeDefined() + expect(code).toContain('{ head, mode: \'server\' }') + }) + it('handles #import', async () => { const code = await transform([ 'import { useSeoMeta } from \'#imports\'', diff --git a/packages/devtools-app/.nuxt/app.config.mjs b/packages/devtools-app/.nuxt/app.config.mjs index aab26c4a7..217957076 100644 --- a/packages/devtools-app/.nuxt/app.config.mjs +++ b/packages/devtools-app/.nuxt/app.config.mjs @@ -312,6 +312,6 @@ if (import.meta.dev && !import.meta.nitro && import.meta.hot) { } /** client-end **/ +import cfg0 from "/home/harlan/pkg/unhead/packages/devtools-app/app/app.config.ts" - -export default /*@__PURE__*/ defuFn(inlineConfig) +export default /*@__PURE__*/ defuFn(cfg0, inlineConfig) diff --git a/packages/devtools-app/.nuxt/components.d.ts b/packages/devtools-app/.nuxt/components.d.ts deleted file mode 100644 index 12e2dd1f6..000000000 --- a/packages/devtools-app/.nuxt/components.d.ts +++ /dev/null @@ -1,322 +0,0 @@ - -import type { DefineComponent, SlotsType } from 'vue' -type IslandComponent<T> = DefineComponent<{}, {refresh: () => Promise<void>}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T - -type HydrationStrategies = { - hydrateOnVisible?: IntersectionObserverInit | true - hydrateOnIdle?: number | true - hydrateOnInteraction?: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap> | true - hydrateOnMediaQuery?: string - hydrateAfter?: number - hydrateWhen?: boolean - hydrateNever?: true -} -type LazyComponent<T> = DefineComponent<HydrationStrategies, {}, {}, {}, {}, {}, {}, { hydrated: () => void }> & T - - -export const UColorModeAvatar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeAvatar.vue")['default'] -export const UColorModeButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeButton.vue")['default'] -export const UColorModeImage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeImage.vue")['default'] -export const UColorModeSelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSelect.vue")['default'] -export const UColorModeSwitch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSwitch.vue")['default'] -export const UAccordion: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Accordion.vue")['default'] -export const UAlert: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue")['default'] -export const UApp: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/App.vue")['default'] -export const UAuthForm: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AuthForm.vue")['default'] -export const UAvatar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Avatar.vue")['default'] -export const UAvatarGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AvatarGroup.vue")['default'] -export const UBadge: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue")['default'] -export const UBanner: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Banner.vue")['default'] -export const UBlogPost: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPost.vue")['default'] -export const UBlogPosts: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPosts.vue")['default'] -export const UBreadcrumb: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Breadcrumb.vue")['default'] -export const UButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Button.vue")['default'] -export const UCalendar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Calendar.vue")['default'] -export const UCard: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Card.vue")['default'] -export const UCarousel: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Carousel.vue")['default'] -export const UChangelogVersion: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersion.vue")['default'] -export const UChangelogVersions: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersions.vue")['default'] -export const UChatMessage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessage.vue")['default'] -export const UChatMessages: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessages.vue")['default'] -export const UChatPalette: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPalette.vue")['default'] -export const UChatPrompt: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPrompt.vue")['default'] -export const UChatPromptSubmit: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPromptSubmit.vue")['default'] -export const UChatReasoning: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatReasoning.vue")['default'] -export const UChatShimmer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatShimmer.vue")['default'] -export const UChatTool: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatTool.vue")['default'] -export const UCheckbox: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue")['default'] -export const UCheckboxGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CheckboxGroup.vue")['default'] -export const UChip: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Chip.vue")['default'] -export const UCollapsible: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Collapsible.vue")['default'] -export const UColorPicker: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ColorPicker.vue")['default'] -export const UCommandPalette: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CommandPalette.vue")['default'] -export const UContainer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Container.vue")['default'] -export const UContextMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue")['default'] -export const UContextMenuContent: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenuContent.vue")['default'] -export const UDashboardGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardGroup.vue")['default'] -export const UDashboardNavbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardNavbar.vue")['default'] -export const UDashboardPanel: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardPanel.vue")['default'] -export const UDashboardResizeHandle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardResizeHandle.vue")['default'] -export const UDashboardSearch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearch.vue")['default'] -export const UDashboardSearchButton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearchButton.vue")['default'] -export const UDashboardSidebar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebar.vue")['default'] -export const UDashboardSidebarCollapse: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarCollapse.vue")['default'] -export const UDashboardSidebarToggle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarToggle.vue")['default'] -export const UDashboardToolbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardToolbar.vue")['default'] -export const UDrawer: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue")['default'] -export const UDropdownMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue")['default'] -export const UDropdownMenuContent: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenuContent.vue")['default'] -export const UEditor: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Editor.vue")['default'] -export const UEditorDragHandle: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorDragHandle.vue")['default'] -export const UEditorEmojiMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorEmojiMenu.vue")['default'] -export const UEditorMentionMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorMentionMenu.vue")['default'] -export const UEditorSuggestionMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorSuggestionMenu.vue")['default'] -export const UEditorToolbar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorToolbar.vue")['default'] -export const UEmpty: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Empty.vue")['default'] -export const UError: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Error.vue")['default'] -export const UFieldGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FieldGroup.vue")['default'] -export const UFileUpload: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FileUpload.vue")['default'] -export const UFooter: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Footer.vue")['default'] -export const UFooterColumns: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FooterColumns.vue")['default'] -export const UForm: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Form.vue")['default'] -export const UFormField: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue")['default'] -export const UHeader: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Header.vue")['default'] -export const UIcon: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Icon.vue")['default'] -export const UInput: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Input.vue")['default'] -export const UInputDate: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputDate.vue")['default'] -export const UInputMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputMenu.vue")['default'] -export const UInputNumber: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue")['default'] -export const UInputTags: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTags.vue")['default'] -export const UInputTime: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTime.vue")['default'] -export const UKbd: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Kbd.vue")['default'] -export const ULink: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Link.vue")['default'] -export const ULinkBase: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/LinkBase.vue")['default'] -export const UMain: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Main.vue")['default'] -export const UMarquee: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Marquee.vue")['default'] -export const UModal: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue")['default'] -export const UNavigationMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue")['default'] -export const UOverlayProvider: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/OverlayProvider.vue")['default'] -export const UPage: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Page.vue")['default'] -export const UPageAnchors: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAnchors.vue")['default'] -export const UPageAside: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAside.vue")['default'] -export const UPageBody: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageBody.vue")['default'] -export const UPageCTA: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCTA.vue")['default'] -export const UPageCard: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCard.vue")['default'] -export const UPageColumns: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageColumns.vue")['default'] -export const UPageFeature: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageFeature.vue")['default'] -export const UPageGrid: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageGrid.vue")['default'] -export const UPageHeader: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHeader.vue")['default'] -export const UPageHero: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHero.vue")['default'] -export const UPageLinks: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLinks.vue")['default'] -export const UPageList: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageList.vue")['default'] -export const UPageLogos: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLogos.vue")['default'] -export const UPageSection: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageSection.vue")['default'] -export const UPagination: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue")['default'] -export const UPinInput: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PinInput.vue")['default'] -export const UPopover: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue")['default'] -export const UPricingPlan: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlan.vue")['default'] -export const UPricingPlans: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlans.vue")['default'] -export const UPricingTable: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingTable.vue")['default'] -export const UProgress: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue")['default'] -export const URadioGroup: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/RadioGroup.vue")['default'] -export const UScrollArea: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ScrollArea.vue")['default'] -export const USelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Select.vue")['default'] -export const USelectMenu: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue")['default'] -export const USeparator: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Separator.vue")['default'] -export const USidebar: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Sidebar.vue")['default'] -export const USkeleton: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Skeleton.vue")['default'] -export const USlideover: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slideover.vue")['default'] -export const USlider: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slider.vue")['default'] -export const UStepper: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Stepper.vue")['default'] -export const USwitch: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue")['default'] -export const UTable: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Table.vue")['default'] -export const UTabs: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue")['default'] -export const UTextarea: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue")['default'] -export const UTheme: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Theme.vue")['default'] -export const UTimeline: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Timeline.vue")['default'] -export const UToast: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toast.vue")['default'] -export const UToaster: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toaster.vue")['default'] -export const UTooltip: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue")['default'] -export const UTree: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tree.vue")['default'] -export const UUser: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/User.vue")['default'] -export const ULocaleSelect: typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/locale/LocaleSelect.vue")['default'] -export const NuxtWelcome: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/welcome.vue")['default'] -export const NuxtLayout: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-layout")['default'] -export const NuxtErrorBoundary: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default'] -export const ClientOnly: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/client-only")['default'] -export const DevOnly: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/dev-only")['default'] -export const ServerPlaceholder: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/server-placeholder")['default'] -export const NuxtLink: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-link")['default'] -export const NuxtLoadingIndicator: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default'] -export const NuxtTime: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-time.vue")['default'] -export const NuxtRouteAnnouncer: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default'] -export const NuxtAnnouncer: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-announcer")['default'] -export const NuxtImg: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtImg'] -export const NuxtPicture: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtPicture'] -export const NuxtPage: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/pages/runtime/page")['default'] -export const NoScript: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['NoScript'] -export const Link: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Link'] -export const Base: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Base'] -export const Title: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Title'] -export const Meta: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Meta'] -export const Style: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Style'] -export const Head: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Head'] -export const Html: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Html'] -export const Body: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Body'] -export const Icon: typeof import("../../../node_modules/.pnpm/@nuxt+icon@2.2.1_magicast@0.5.2_vite@8.0.6_vue@3.5.32_typescript@6.0.2_/node_modules/@nuxt/icon/dist/runtime/components/index")['default'] -export const ColorScheme: typeof import("../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/component.vue3.vue")['default'] -export const NuxtIsland: typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-island")['default'] -export const LazyUColorModeAvatar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeAvatar.vue")['default']> -export const LazyUColorModeButton: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeButton.vue")['default']> -export const LazyUColorModeImage: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeImage.vue")['default']> -export const LazyUColorModeSelect: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSelect.vue")['default']> -export const LazyUColorModeSwitch: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/color-mode/ColorModeSwitch.vue")['default']> -export const LazyUAccordion: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Accordion.vue")['default']> -export const LazyUAlert: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue")['default']> -export const LazyUApp: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/App.vue")['default']> -export const LazyUAuthForm: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AuthForm.vue")['default']> -export const LazyUAvatar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Avatar.vue")['default']> -export const LazyUAvatarGroup: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/AvatarGroup.vue")['default']> -export const LazyUBadge: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue")['default']> -export const LazyUBanner: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Banner.vue")['default']> -export const LazyUBlogPost: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPost.vue")['default']> -export const LazyUBlogPosts: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/BlogPosts.vue")['default']> -export const LazyUBreadcrumb: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Breadcrumb.vue")['default']> -export const LazyUButton: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Button.vue")['default']> -export const LazyUCalendar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Calendar.vue")['default']> -export const LazyUCard: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Card.vue")['default']> -export const LazyUCarousel: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Carousel.vue")['default']> -export const LazyUChangelogVersion: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersion.vue")['default']> -export const LazyUChangelogVersions: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChangelogVersions.vue")['default']> -export const LazyUChatMessage: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessage.vue")['default']> -export const LazyUChatMessages: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatMessages.vue")['default']> -export const LazyUChatPalette: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPalette.vue")['default']> -export const LazyUChatPrompt: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPrompt.vue")['default']> -export const LazyUChatPromptSubmit: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatPromptSubmit.vue")['default']> -export const LazyUChatReasoning: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatReasoning.vue")['default']> -export const LazyUChatShimmer: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatShimmer.vue")['default']> -export const LazyUChatTool: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ChatTool.vue")['default']> -export const LazyUCheckbox: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue")['default']> -export const LazyUCheckboxGroup: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CheckboxGroup.vue")['default']> -export const LazyUChip: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Chip.vue")['default']> -export const LazyUCollapsible: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Collapsible.vue")['default']> -export const LazyUColorPicker: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ColorPicker.vue")['default']> -export const LazyUCommandPalette: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/CommandPalette.vue")['default']> -export const LazyUContainer: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Container.vue")['default']> -export const LazyUContextMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue")['default']> -export const LazyUContextMenuContent: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ContextMenuContent.vue")['default']> -export const LazyUDashboardGroup: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardGroup.vue")['default']> -export const LazyUDashboardNavbar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardNavbar.vue")['default']> -export const LazyUDashboardPanel: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardPanel.vue")['default']> -export const LazyUDashboardResizeHandle: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardResizeHandle.vue")['default']> -export const LazyUDashboardSearch: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearch.vue")['default']> -export const LazyUDashboardSearchButton: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSearchButton.vue")['default']> -export const LazyUDashboardSidebar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebar.vue")['default']> -export const LazyUDashboardSidebarCollapse: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarCollapse.vue")['default']> -export const LazyUDashboardSidebarToggle: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardSidebarToggle.vue")['default']> -export const LazyUDashboardToolbar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DashboardToolbar.vue")['default']> -export const LazyUDrawer: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue")['default']> -export const LazyUDropdownMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue")['default']> -export const LazyUDropdownMenuContent: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenuContent.vue")['default']> -export const LazyUEditor: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Editor.vue")['default']> -export const LazyUEditorDragHandle: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorDragHandle.vue")['default']> -export const LazyUEditorEmojiMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorEmojiMenu.vue")['default']> -export const LazyUEditorMentionMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorMentionMenu.vue")['default']> -export const LazyUEditorSuggestionMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorSuggestionMenu.vue")['default']> -export const LazyUEditorToolbar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/EditorToolbar.vue")['default']> -export const LazyUEmpty: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Empty.vue")['default']> -export const LazyUError: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Error.vue")['default']> -export const LazyUFieldGroup: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FieldGroup.vue")['default']> -export const LazyUFileUpload: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FileUpload.vue")['default']> -export const LazyUFooter: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Footer.vue")['default']> -export const LazyUFooterColumns: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FooterColumns.vue")['default']> -export const LazyUForm: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Form.vue")['default']> -export const LazyUFormField: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue")['default']> -export const LazyUHeader: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Header.vue")['default']> -export const LazyUIcon: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Icon.vue")['default']> -export const LazyUInput: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Input.vue")['default']> -export const LazyUInputDate: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputDate.vue")['default']> -export const LazyUInputMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputMenu.vue")['default']> -export const LazyUInputNumber: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputNumber.vue")['default']> -export const LazyUInputTags: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTags.vue")['default']> -export const LazyUInputTime: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/InputTime.vue")['default']> -export const LazyUKbd: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Kbd.vue")['default']> -export const LazyULink: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Link.vue")['default']> -export const LazyULinkBase: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/LinkBase.vue")['default']> -export const LazyUMain: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Main.vue")['default']> -export const LazyUMarquee: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Marquee.vue")['default']> -export const LazyUModal: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue")['default']> -export const LazyUNavigationMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue")['default']> -export const LazyUOverlayProvider: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/OverlayProvider.vue")['default']> -export const LazyUPage: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Page.vue")['default']> -export const LazyUPageAnchors: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAnchors.vue")['default']> -export const LazyUPageAside: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageAside.vue")['default']> -export const LazyUPageBody: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageBody.vue")['default']> -export const LazyUPageCTA: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCTA.vue")['default']> -export const LazyUPageCard: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageCard.vue")['default']> -export const LazyUPageColumns: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageColumns.vue")['default']> -export const LazyUPageFeature: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageFeature.vue")['default']> -export const LazyUPageGrid: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageGrid.vue")['default']> -export const LazyUPageHeader: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHeader.vue")['default']> -export const LazyUPageHero: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageHero.vue")['default']> -export const LazyUPageLinks: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLinks.vue")['default']> -export const LazyUPageList: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageList.vue")['default']> -export const LazyUPageLogos: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageLogos.vue")['default']> -export const LazyUPageSection: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PageSection.vue")['default']> -export const LazyUPagination: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Pagination.vue")['default']> -export const LazyUPinInput: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PinInput.vue")['default']> -export const LazyUPopover: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue")['default']> -export const LazyUPricingPlan: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlan.vue")['default']> -export const LazyUPricingPlans: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingPlans.vue")['default']> -export const LazyUPricingTable: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/PricingTable.vue")['default']> -export const LazyUProgress: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue")['default']> -export const LazyURadioGroup: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/RadioGroup.vue")['default']> -export const LazyUScrollArea: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/ScrollArea.vue")['default']> -export const LazyUSelect: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Select.vue")['default']> -export const LazyUSelectMenu: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue")['default']> -export const LazyUSeparator: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Separator.vue")['default']> -export const LazyUSidebar: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Sidebar.vue")['default']> -export const LazyUSkeleton: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Skeleton.vue")['default']> -export const LazyUSlideover: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slideover.vue")['default']> -export const LazyUSlider: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Slider.vue")['default']> -export const LazyUStepper: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Stepper.vue")['default']> -export const LazyUSwitch: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue")['default']> -export const LazyUTable: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Table.vue")['default']> -export const LazyUTabs: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue")['default']> -export const LazyUTextarea: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue")['default']> -export const LazyUTheme: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Theme.vue")['default']> -export const LazyUTimeline: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Timeline.vue")['default']> -export const LazyUToast: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toast.vue")['default']> -export const LazyUToaster: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Toaster.vue")['default']> -export const LazyUTooltip: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue")['default']> -export const LazyUTree: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/Tree.vue")['default']> -export const LazyUUser: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/User.vue")['default']> -export const LazyULocaleSelect: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/components/locale/LocaleSelect.vue")['default']> -export const LazyNuxtWelcome: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/welcome.vue")['default']> -export const LazyNuxtLayout: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-layout")['default']> -export const LazyNuxtErrorBoundary: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue")['default']> -export const LazyClientOnly: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/client-only")['default']> -export const LazyDevOnly: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/dev-only")['default']> -export const LazyServerPlaceholder: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/server-placeholder")['default']> -export const LazyNuxtLink: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-link")['default']> -export const LazyNuxtLoadingIndicator: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-loading-indicator")['default']> -export const LazyNuxtTime: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-time.vue")['default']> -export const LazyNuxtRouteAnnouncer: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-route-announcer")['default']> -export const LazyNuxtAnnouncer: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-announcer")['default']> -export const LazyNuxtImg: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtImg']> -export const LazyNuxtPicture: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-stubs")['NuxtPicture']> -export const LazyNuxtPage: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/pages/runtime/page")['default']> -export const LazyNoScript: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['NoScript']> -export const LazyLink: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Link']> -export const LazyBase: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Base']> -export const LazyTitle: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Title']> -export const LazyMeta: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Meta']> -export const LazyStyle: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Style']> -export const LazyHead: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Head']> -export const LazyHtml: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Html']> -export const LazyBody: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/head/runtime/components")['Body']> -export const LazyIcon: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxt+icon@2.2.1_magicast@0.5.2_vite@8.0.6_vue@3.5.32_typescript@6.0.2_/node_modules/@nuxt/icon/dist/runtime/components/index")['default']> -export const LazyColorScheme: LazyComponent<typeof import("../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/component.vue3.vue")['default']> -export const LazyNuxtIsland: LazyComponent<typeof import("../../../node_modules/.pnpm/nuxt@4.4.2_@babel+core@7.29.0_@babel+plugin-syntax-jsx@7.27.1_@babel+core@7.29.0__@emna_17316aa2baa665c2560f0c35a00aa6b3/node_modules/nuxt/dist/app/components/nuxt-island")['default']> - -export const componentNames: string[] diff --git a/packages/devtools-app/.nuxt/imports.d.ts b/packages/devtools-app/.nuxt/imports.d.ts deleted file mode 100644 index 67ea0eb79..000000000 --- a/packages/devtools-app/.nuxt/imports.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -export { useScriptTriggerConsent, useScriptEventPage, useScriptTriggerElement, useScript, useScriptGoogleAnalytics, useScriptPlausibleAnalytics, useScriptCrisp, useScriptClarity, useScriptCloudflareWebAnalytics, useScriptFathomAnalytics, useScriptMatomoAnalytics, useScriptGoogleTagManager, useScriptGoogleAdsense, useScriptSegment, useScriptMetaPixel, useScriptXPixel, useScriptIntercom, useScriptHotjar, useScriptStripe, useScriptLemonSqueezy, useScriptVimeoPlayer, useScriptYouTubePlayer, useScriptGoogleMaps, useScriptNpm, useScriptUmamiAnalytics, useScriptSnapchatPixel, useScriptRybbitAnalytics, useScriptDatabuddyAnalytics, useScriptRedditPixel, useScriptPayPal } from '#app/composables/script-stubs'; -export { isVue2, isVue3 } from 'vue-demi'; -export { defineNuxtLink } from '#app/components/nuxt-link'; -export { useNuxtApp, tryUseNuxtApp, defineNuxtPlugin, definePayloadPlugin, useRuntimeConfig, defineAppConfig } from '#app/nuxt'; -export { useAppConfig, updateAppConfig } from '#app/config'; -export { defineNuxtComponent } from '#app/composables/component'; -export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, createUseAsyncData } from '#app/composables/asyncData'; -export { useHydration } from '#app/composables/hydrate'; -export { callOnce } from '#app/composables/once'; -export { useState, clearNuxtState } from '#app/composables/state'; -export { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error'; -export { useFetch, useLazyFetch, createUseFetch } from '#app/composables/fetch'; -export { useCookie, refreshCookie } from '#app/composables/cookie'; -export { onPrehydrate, prerenderRoutes, useRequestHeader, useRequestHeaders, useResponseHeader, useRequestEvent, useRequestFetch, setResponseStatus } from '#app/composables/ssr'; -export { onNuxtReady } from '#app/composables/ready'; -export { preloadComponents, prefetchComponents, preloadRouteComponents } from '#app/composables/preload'; -export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter } from '#app/composables/router'; -export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from '#app/composables/payload'; -export { useLoadingIndicator } from '#app/composables/loading-indicator'; -export { getAppManifest, getRouteRules } from '#app/composables/manifest'; -export { reloadNuxtApp } from '#app/composables/chunk'; -export { useRequestURL } from '#app/composables/url'; -export { usePreviewMode } from '#app/composables/preview'; -export { useRouteAnnouncer } from '#app/composables/route-announcer'; -export { useAnnouncer } from '#app/composables/announcer'; -export { useRuntimeHook } from '#app/composables/runtime-hook'; -export { useHead, useHeadSafe, useServerHeadSafe, useServerHead, useSeoMeta, useServerSeoMeta, injectHead } from '#app/composables/head'; -export { onBeforeRouteLeave, onBeforeRouteUpdate, useLink } from 'vue-router'; -export { withCtx, withDirectives, withKeys, withMemo, withModifiers, withScopeId, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onServerPrefetch, onUnmounted, onUpdated, computed, customRef, isProxy, isReactive, isReadonly, isRef, markRaw, proxyRefs, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, watch, watchEffect, watchPostEffect, watchSyncEffect, onWatcherCleanup, isShallow, effect, effectScope, getCurrentScope, onScopeDispose, defineComponent, defineAsyncComponent, resolveComponent, getCurrentInstance, h, inject, hasInjectionContext, nextTick, provide, toValue, useModel, useAttrs, useCssModule, useCssVars, useSlots, useTransitionState, useId, useTemplateRef, useShadowRoot, Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'; -export { requestIdleCallback, cancelIdleCallback } from '#app/compat/idle-callback'; -export { setInterval } from '#app/compat/interval'; -export { computedAsync, asyncComputed, computedEager, eagerComputed, computedInject, computedWithControl, controlledComputed, createEventHook, createGlobalState, createInjectionState, createRef, createReusableTemplate, createSharedComposable, createTemplatePromise, createUnrefFn, extendRef, injectLocal, isDefined, makeDestructurable, onClickOutside, onElementRemoval, onKeyStroke, onLongPress, onStartTyping, provideLocal, reactify, createReactiveFn, reactifyObject, reactiveComputed, reactiveOmit, reactivePick, refAutoReset, autoResetRef, refDebounced, useDebounce, debouncedRef, refDefault, refManualReset, refThrottled, useThrottle, throttledRef, refWithControl, controlledRef, syncRef, syncRefs, templateRef, toReactive, tryOnBeforeMount, tryOnBeforeUnmount, tryOnMounted, tryOnScopeDispose, tryOnUnmounted, unrefElement, until, useActiveElement, useAnimate, useArrayDifference, useArrayEvery, useArrayFilter, useArrayFind, useArrayFindIndex, useArrayFindLast, useArrayIncludes, useArrayJoin, useArrayMap, useArrayReduce, useArraySome, useArrayUnique, useAsyncQueue, useAsyncState, useBase64, useBattery, useBluetooth, useBreakpoints, useBroadcastChannel, useBrowserLocation, useCached, useClipboard, useClipboardItems, useCloned, useConfirmDialog, useCountdown, useCounter, useCssSupports, useCssVar, useCurrentElement, useCycleList, useDark, useDateFormat, useDebouncedRefHistory, useDebounceFn, useDeviceMotion, useDeviceOrientation, useDevicePixelRatio, useDevicesList, useDisplayMedia, useDocumentVisibility, useDraggable, useDropZone, useElementBounding, useElementByPoint, useElementHover, useElementSize, useElementVisibility, useEventBus, useEventListener, useEventSource, useEyeDropper, useFavicon, useFileDialog, useFileSystemAccess, useFocus, useFocusWithin, useFps, useFullscreen, useGamepad, useGeolocation, useIdle, useInfiniteScroll, useIntersectionObserver, useInterval, useIntervalFn, useKeyModifier, useLastChanged, useLocalStorage, useMagicKeys, useManualRefHistory, useMediaControls, useMediaQuery, useMemoize, useMemory, useMounted, useMouse, useMouseInElement, useMousePressed, useMutationObserver, useNavigatorLanguage, useNetwork, useNow, useObjectUrl, useOffsetPagination, useOnline, usePageLeave, useParallax, useParentElement, usePerformanceObserver, usePermission, usePointer, usePointerLock, usePointerSwipe, usePreferredColorScheme, usePreferredContrast, usePreferredDark, usePreferredLanguages, usePreferredReducedMotion, usePreferredReducedTransparency, usePrevious, useRafFn, useRefHistory, useResizeObserver, useScreenOrientation, useScreenSafeArea, useScriptTag, useScroll, useScrollLock, useSessionStorage, useShare, useSorted, useSpeechRecognition, useSpeechSynthesis, useSSRWidth, useStepper, useStorageAsync, useStyleTag, useSupported, useSwipe, useTemplateRefsList, useTextareaAutosize, useTextDirection, useTextSelection, useThrottledRefHistory, useThrottleFn, useTimeAgo, useTimeAgoIntl, useTimeout, useTimeoutFn, useTimeoutPoll, useTimestamp, useTitle, useToggle, useToNumber, useToString, useTransition, useUrlSearchParams, useUserMedia, useVibrate, useVirtualList, useVModel, useVModels, useWakeLock, useWebNotification, useWebSocket, useWebWorker, useWebWorkerFn, useWindowFocus, useWindowScroll, useWindowSize, watchArray, watchAtMost, watchDebounced, debouncedWatch, watchDeep, watchIgnorable, ignorableWatch, watchImmediate, watchOnce, watchPausable, pausableWatch, watchThrottled, throttledWatch, watchTriggerable, watchWithFilter, whenever } from '@vueuse/core'; -export { definePageMeta, PageMeta } from '#app/composables/pages'; -export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration'; -export { colorMode, useDevtoolsConnection } from '../composables/rpc'; -export { state, isConnected, syncState, SeoOverview, SerializedEntry, SerializedTag, SerializedScript, SerializedValidationRule, UnheadDevtoolsState } from '../composables/state'; -export { SEO_LIMITS, estimatePixelWidth, titleColor, descColor } from '../composables/tools'; -export { defineLocale, extendLocale } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale'; -export { defineShortcuts, extractShortcuts } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts'; -export { useContentSearch } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch'; -export { useFileUpload } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload'; -export { useFormField } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useFormField'; -export { useKbd } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useKbd'; -export { useOverlay } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay'; -export { useResizable } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useResizable'; -export { useScrollShadow } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useScrollShadow'; -export { useScrollspy } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy'; -export { useToast } from '../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/composables/useToast'; -export { useColorMode } from '../../../node_modules/.pnpm/@nuxtjs+color-mode@3.5.2_magicast@0.5.2/node_modules/@nuxtjs/color-mode/dist/runtime/composables'; \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json b/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/096c6b6e-a3cb-48e9-9cf3-71a048d80dfc.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json b/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/0a14115d-d308-4b99-bd05-d7b0c3f37b8a.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json b/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/381b0558-98b7-448d-9168-38957e42e433.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json b/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/4cbfe73a-f297-47d0-bb9a-71b456d7d08a.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json b/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/8251125c-23c7-4428-a540-3c662e362e93.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json b/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/c2629906-49d7-48f6-8234-c88fca0b6c44.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json b/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/packages/devtools-app/.nuxt/manifest/meta/cb61506d-f6b8-4d8c-b9e3-4c2a91761269.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs b/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs deleted file mode 100644 index 95287ba80..000000000 --- a/packages/devtools-app/.nuxt/nuxt-icon-server-bundle.mjs +++ /dev/null @@ -1,13 +0,0 @@ -function createRemoteCollection(fetchEndpoint) { - let _cache - return async () => { - if (_cache) - return _cache - const res = await fetch(fetchEndpoint).then(r => r.json()) - _cache = res - return res - } -} - -export const collections = { -} \ No newline at end of file diff --git a/packages/devtools-app/.nuxt/nuxt.d.ts b/packages/devtools-app/.nuxt/nuxt.d.ts deleted file mode 100644 index dfcccabfd..000000000 --- a/packages/devtools-app/.nuxt/nuxt.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/// <reference types="@nuxt/ui" /> -/// <reference types="@vueuse/nuxt" /> -/// <reference types="@nuxt/telemetry" /> -/// <reference path="types/nitro-layouts.d.ts" /> -/// <reference path="types/builder-env.d.ts" /> -/// <reference path="types/plugins.d.ts" /> -/// <reference path="types/build.d.ts" /> -/// <reference path="types/app.config.d.ts" /> -/// <reference path="types/runtime-config.d.ts" /> -/// <reference types="nuxt/app" /> -/// <reference path="../../../node_modules/.pnpm/@nuxt+nitro-server@4.4.2_@babel+core@7.29.0_db0@0.3.4_encoding@0.1.13_ioredis@5.10.1_ma_1683098aeb158903108dde4229907837/node_modules/@nuxt/nitro-server/dist/index.d.mts" /> -/// <reference path="types/ui.d.ts" /> -/// <reference path="../../../node_modules/.pnpm/@nuxt+ui@4.6.1_@tiptap+extensions@3.22.2_@tiptap+core@3.22.2_@tiptap+pm@3.22.2__@tiptap_8f3afea1727ed5390a59ed94dec11840/node_modules/@nuxt/ui/dist/runtime/types/app.config.d.ts" /> -/// <reference types="vue-router" /> -/// <reference path="types/middleware.d.ts" /> -/// <reference path="types/nitro-middleware.d.ts" /> -/// <reference path="types/layouts.d.ts" /> -/// <reference path="types/components.d.ts" /> -/// <reference path="imports.d.ts" /> -/// <reference path="types/imports.d.ts" /> -/// <reference path="schema/nuxt.schema.d.ts" /> -/// <reference path="types/nitro.d.ts" /> - -export {} diff --git a/packages/devtools-app/.nuxt/nuxt.node.d.ts b/packages/devtools-app/.nuxt/nuxt.node.d.ts deleted file mode 100644 index ae6e2dec4..000000000 --- a/packages/devtools-app/.nuxt/nuxt.node.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// <reference types="@nuxt/ui" /> -/// <reference types="@vueuse/nuxt" /> -/// <reference types="@nuxt/telemetry" /> -/// <reference path="types/nitro-layouts.d.ts" /> -/// <reference path="types/modules.d.ts" /> -/// <reference path="types/runtime-config.d.ts" /> -/// <reference path="types/app.config.d.ts" /> -/// <reference types="nuxt" /> -/// <reference path="../../../node_modules/.pnpm/@nuxt+vite-builder@4.4.2_cdb12d2d8da68433b7c16592f440f24c/node_modules/@nuxt/vite-builder/dist/index.d.mts" /> -/// <reference path="../../../node_modules/.pnpm/@nuxt+nitro-server@4.4.2_@babel+core@7.29.0_db0@0.3.4_encoding@0.1.13_ioredis@5.10.1_ma_1683098aeb158903108dde4229907837/node_modules/@nuxt/nitro-server/dist/index.d.mts" /> -/// <reference path="types/nitro-middleware.d.ts" /> -/// <reference path="schema/nuxt.schema.d.ts" /> - -export {} diff --git a/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs b/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs deleted file mode 100644 index f8de1bfd0..000000000 --- a/packages/devtools-app/.nuxt/prerender/chunks/_/error-500.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { escapeHtml } from 'file:///home/harlan/pkg/unhead/.claude/worktrees/feat-vite-devtools/node_modules/.pnpm/@vue+shared@3.5.32/node_modules/@vue/shared/dist/shared.cjs.prod.js'; - -const _messages = { - "appName": "Nuxt", - "status": 500, - "statusText": "Internal server error", - "description": "This page is temporarily unavailable.", - "refresh": "Refresh this page" -}; -const template = (messages) => { - messages = { - ..._messages, - ...messages - }; - return "<!DOCTYPE html><html lang=\"en\"><head><title>" + escapeHtml(messages.status) + " - " + escapeHtml(messages.statusText) + " | " + escapeHtml(messages.appName) + " - - - - diff --git a/packages/devtools-app/app/app.config.ts b/packages/devtools-app/app/app.config.ts new file mode 100644 index 000000000..a4e6a6b22 --- /dev/null +++ b/packages/devtools-app/app/app.config.ts @@ -0,0 +1,55 @@ +export default defineAppConfig({ + ui: { + colors: { + primary: 'amber', + neutral: 'stone', + }, + badge: { + defaultVariants: { + variant: 'subtle' as const, + }, + }, + card: { + slots: { + root: 'rounded-xl', + }, + }, + tabs: { + trigger: 'leading-8', + label: 'font-medium', + list: 'h-[48px] gap-5', + variants: { + size: { md: { trigger: 'px-0 pt-0' } }, + }, + }, + icons: { + caution: 'i-carbon-warning-alt', + copy: 'i-carbon-copy', + dark: 'i-carbon-moon', + document: 'i-carbon-document', + external: 'i-carbon-launch', + hash: 'i-carbon-hashtag', + light: 'i-carbon-sun', + menu: 'i-carbon-menu', + next: 'i-carbon-arrow-right', + note: 'i-carbon-information', + prev: 'i-carbon-arrow-left', + system: 'i-carbon-screen', + tip: 'i-carbon-idea', + warning: 'i-carbon-warning', + chevronDoubleLeft: 'i-carbon-chevron-double-left', + chevronDoubleRight: 'i-carbon-chevron-double-right', + chevronDown: 'i-carbon-chevron-down', + chevronLeft: 'i-carbon-chevron-left', + chevronRight: 'i-carbon-chevron-right', + arrowLeft: 'i-carbon-arrow-left', + arrowRight: 'i-carbon-arrow-right', + check: 'i-carbon-checkmark', + close: 'i-carbon-close', + ellipsis: 'i-carbon-overflow-menu-horizontal', + loading: 'i-carbon-loading', + minus: 'i-carbon-subtract', + search: 'i-carbon-search', + }, + }, +}) diff --git a/packages/devtools-app/app/app.vue b/packages/devtools-app/app/app.vue new file mode 100644 index 000000000..e9369568d --- /dev/null +++ b/packages/devtools-app/app/app.vue @@ -0,0 +1,72 @@ + + +