From cdb5d085439a6f2272b495101036d2bfa9fb30cc Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Wed, 18 Mar 2026 20:36:27 +0900 Subject: [PATCH 01/16] feat: add human-readable auth IDs and multiple auth modes Add human-readable auth IDs, configurable auth passwords, and an auth URL endpoint: - Replace random nanoid with human-id (e.g. apple-banana-something-words) - Add clientAuthPasswords config for pre-approved auth credentials - Server generates temporary auth IDs for clickable auth URLs (/.devtools/auth?id=xxx) - Terminal prompt shows auth URL and aborts on timeout (60s), URL usage, or new auth request - Add BroadcastChannel to sync auth updates across browser tabs - Add password input UI to ViewBuiltinClientAuthNotice for manual auth entry - Add auth-state.ts module to manage pending auth state and temp ID consumption All tests pass, build succeeds, and the feature is fully functional. Co-Authored-By: Claude Haiku 4.5 --- packages/core/package.json | 2 + .../client/webcomponents/.generated/css.ts | 2 +- .../ViewBuiltinClientAuthNotice.vue | 29 +++++ packages/core/src/node/auth-state.ts | 87 +++++++++++++ packages/core/src/node/config.ts | 8 ++ packages/core/src/node/rpc/anonymous/auth.ts | 117 ++++++++++++++---- packages/core/src/node/server.ts | 76 +++++++++++- packages/core/src/node/ws.ts | 4 + packages/core/tsdown.config.ts | 1 + packages/kit/package.json | 2 + packages/kit/src/client/rpc.ts | 16 ++- packages/kit/tsdown.config.ts | 1 + pnpm-lock.yaml | 85 +++++++------ pnpm-workspace.yaml | 1 + 14 files changed, 369 insertions(+), 62 deletions(-) create mode 100644 packages/core/src/node/auth-state.ts diff --git a/packages/core/package.json b/packages/core/package.json index be94a6c4..f215f820 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -77,6 +77,7 @@ "@xterm/addon-fit": "catalog:frontend", "@xterm/xterm": "catalog:frontend", "dompurify": "catalog:frontend", + "human-id": "catalog:inlined", "tsdown": "catalog:build", "typescript": "catalog:devtools", "unplugin-vue": "catalog:build", @@ -105,6 +106,7 @@ "@clack/core": "1.1.0", "@clack/prompts": "1.1.0", "ansis": "4.2.0", + "human-id": "4.1.3", "get-port-please": "3.2.0" } } diff --git a/packages/core/src/client/webcomponents/.generated/css.ts b/packages/core/src/client/webcomponents/.generated/css.ts index d17bd575..2cda170d 100644 --- a/packages/core/src/client/webcomponents/.generated/css.ts +++ b/packages/core/src/client/webcomponents/.generated/css.ts @@ -1,3 +1,3 @@ /* eslint-disable eslint-comments/no-unlimited-disable */ /* eslint-disable */ -export default "*{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:before{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:after{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:before{--un-content:\"\"}:after{--un-content:\"\"}html{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button{-webkit-appearance:button;background-color:transparent;background-image:none}[type=button]{-webkit-appearance:button;background-color:transparent;background-image:none}[type=reset]{-webkit-appearance:button;background-color:transparent;background-image:none}[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::placeholder{opacity:1;color:#9ca3af}textarea::placeholder{opacity:1;color:#9ca3af}button{cursor:pointer}[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.xterm{cursor:text;user-select:none;position:relative}.xterm.focus{outline:none}.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;top:0;bottom:0;left:0;right:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-message{z-index:10;color:transparent;pointer-events:none;position:absolute;top:0;bottom:0;left:0;right:0}.xterm .xterm-accessibility:not(.debug){z-index:10;color:transparent;pointer-events:none;position:absolute;top:0;bottom:0;left:0;right:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre;font-family:monospace}.xterm .xterm-accessibility-tree>div{transform-origin:0;width:fit-content}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:underline double}.xterm-underline-3{text-decoration:underline wavy}.xterm-underline-4{text-decoration:underline dotted}.xterm-underline-5{text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;z-index:11;background:0 0;transition:opacity .1s linear}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{display:none;position:absolute}.xterm .xterm-scrollable-element>.shadow.top{width:100%;height:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;display:block;top:0;left:3px}.xterm .xterm-scrollable-element>.shadow.left{width:3px;height:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;display:block;top:3px;left:0}.xterm .xterm-scrollable-element>.shadow.top-left-corner{width:3px;height:3px;display:block;top:0;left:0}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset}:root{--un-text-opacity:100%}#vite-devtools-anchor{z-index:2147483644;box-sizing:border-box;transform-origin:50%;width:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:15px;position:fixed;transform:translate(-50%,-50%)rotate(0)}#vite-devtools-anchor #vite-devtools-dock-container{width:max-content;min-width:100px;height:40px;display:flex;position:absolute;top:0;left:0;transform:translate(-50%,-50%)}#vite-devtools-anchor.vite-devtools-vertical #vite-devtools-dock-container{transition-property:all;transition-duration:.5s;transition-timing-function:cubic-bezier(.4,0,.2,1);transform:translate(-50%,-50%)rotate(90deg)}#vite-devtools-anchor #vite-devtools-dock{touch-action:none;user-select:none;--vdt-backdrop-blur:blur(7px);height:40px;backdrop-filter:var(--vdt-backdrop-blur) var(--vdt-backdrop-brightness) var(--vdt-backdrop-contrast) var(--vdt-backdrop-grayscale) var(--vdt-backdrop-hue-rotate) var(--vdt-backdrop-invert) var(--vdt-backdrop-opacity) var(--vdt-backdrop-saturate) var(--vdt-backdrop-sepia);--vdt-text-opacity:1;color:rgba(51,51,51,var(--vdt-text-opacity));--vdt-shadow:var(--vdt-shadow-inset) 0 1px 3px 0 var(--vdt-shadow-color,rgba(0,0,0,.1)),var(--vdt-shadow-inset) 0 1px 2px -1px var(--vdt-shadow-color,rgba(0,0,0,.1));box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow);width:calc-size(max-content, size);background-color:rgba(255,255,255,.5);-webkit-border-radius:9999px;border-radius:9999px;margin:auto;transition-property:all;transition-duration:.5s;transition-timing-function:cubic-bezier(.4,0,.2,1)}@media (prefers-color-scheme:dark){#vite-devtools-anchor #vite-devtools-dock{--vdt-text-opacity:1;color:rgba(255,255,255,var(--vdt-text-opacity));background-color:rgba(17,17,17,.5)}}#vite-devtools-anchor.vite-devtools-minimized #vite-devtools-dock{width:22px;height:22px;padding:2px 0}#vite-devtools-anchor.vite-devtools-minimized .vite-devtools-dock-bracket{opacity:.5;width:.375rem}#vite-devtools-anchor:hover #vite-devtools-glowing{opacity:.6}#vite-devtools-anchor #vite-devtools-glowing{pointer-events:none;z-index:-1;opacity:0;--vdt-blur:blur(60px);width:160px;height:160px;filter:var(--vdt-blur) var(--vdt-brightness) var(--vdt-contrast) var(--vdt-drop-shadow) var(--vdt-grayscale) var(--vdt-hue-rotate) var(--vdt-invert) var(--vdt-saturate) var(--vdt-sepia);background-image:linear-gradient(45deg,#61d9ff,#7a23a1,#715ebd);-webkit-border-radius:9999px;border-radius:9999px;transition-property:all;transition-duration:1s;transition-timing-function:cubic-bezier(0,0,.2,1);position:absolute;top:0;left:0;transform:translate(-50%,-50%)}@media print{#vite-devtools-anchor{display:none}}.vite-devtools-resize-handle-horizontal{cursor:ns-resize;-webkit-border-radius:.375rem;border-radius:.375rem;height:10px;margin-top:-5px;margin-bottom:-5px;position:absolute;left:6px;right:6px}.vite-devtools-resize-handle-vertical{cursor:ew-resize;-webkit-border-radius:.375rem;border-radius:.375rem;width:10px;margin-left:-5px;margin-right:-5px;position:absolute;top:6px;bottom:0}.vite-devtools-resize-handle-corner{-webkit-border-radius:.375rem;border-radius:.375rem;width:14px;height:14px;margin:-6px;position:absolute}.vite-devtools-resize-handle{z-index:30}.vite-devtools-resize-handle:hover{background-color:rgba(156,163,175,.1)}*{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }:before{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }:after{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }::backdrop{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }.i-carbon-clean{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z'/%3E%3Cpath fill='currentColor' d='M17.003 20a4.9 4.9 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.7 5.7 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2m-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848M15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.6 16.6 0 0 1 10 24H8a17.3 17.3 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13 13 0 0 0 17.596 28Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-fluent-emoji-flat-warning{background:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='none'%3E%3Cpath fill='%23FFB02E' d='m14.839 5.668l-12.66 21.93c-.51.89.13 2.01 1.16 2.01h25.32c1.03 0 1.67-1.11 1.16-2.01l-12.66-21.93c-.52-.89-1.8-.89-2.32 0'/%3E%3Cpath fill='%23000' d='M14.599 21.498a1.4 1.4 0 1 0 2.8-.01v-9.16c0-.77-.62-1.4-1.4-1.4c-.77 0-1.4.62-1.4 1.4zm2.8 3.98a1.4 1.4 0 1 1-2.8 0a1.4 1.4 0 0 1 2.8 0'/%3E%3C/g%3E%3C/svg%3E\") 0 0/100% 100% no-repeat;width:1em;height:1em}.i-ph-arrow-clockwise{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M240 56v48a8 8 0 0 1-8 8h-48a8 8 0 0 1 0-16h27.4l-26.59-24.36l-.25-.24a80 80 0 1 0-1.67 114.78a8 8 0 0 1 11 11.63A95.44 95.44 0 0 1 128 224h-1.32a96 96 0 1 1 69.07-164L224 85.8V56a8 8 0 1 1 16 0'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrow-clockwise-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 128a88 88 0 1 1-88-88a88 88 0 0 1 88 88' opacity='.2'/%3E%3Cpath d='M240 56v48a8 8 0 0 1-8 8h-48a8 8 0 0 1 0-16h27.4l-26.59-24.36l-.25-.24a80 80 0 1 0-1.67 114.78a8 8 0 0 1 11 11.63A95.44 95.44 0 0 1 128 224h-1.32a96 96 0 1 1 69.07-164L224 85.8V56a8 8 0 1 1 16 0'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrow-counter-clockwise{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M224 128a96 96 0 0 1-94.71 96H128a95.38 95.38 0 0 1-65.9-26.2a8 8 0 0 1 11-11.63a80 80 0 1 0-1.67-114.78a3 3 0 0 1-.26.25L44.59 96H72a8 8 0 0 1 0 16H24a8 8 0 0 1-8-8V56a8 8 0 0 1 16 0v29.8L60.25 60A96 96 0 0 1 224 128'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrow-square-out-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M184 80v128a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8V80a8 8 0 0 1 8-8h128a8 8 0 0 1 8 8' opacity='.2'/%3E%3Cpath d='M224 104a8 8 0 0 1-16 0V59.32l-66.33 66.34a8 8 0 0 1-11.32-11.32L196.68 48H152a8 8 0 0 1 0-16h64a8 8 0 0 1 8 8Zm-40 24a8 8 0 0 0-8 8v72H48V80h72a8 8 0 0 0 0-16H48a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-72a8 8 0 0 0-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrows-counter-clockwise-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 128a88 88 0 1 1-88-88a88 88 0 0 1 88 88' opacity='.2'/%3E%3Cpath d='M88 104H40a8 8 0 0 1-8-8V48a8 8 0 0 1 16 0v28.69l14.63-14.63A95.43 95.43 0 0 1 130 33.94h.53a95.36 95.36 0 0 1 67.07 27.33a8 8 0 0 1-11.18 11.44a79.52 79.52 0 0 0-55.89-22.77h-.45a79.56 79.56 0 0 0-56.14 23.43L59.31 88H88a8 8 0 0 1 0 16m128 48h-48a8 8 0 0 0 0 16h28.69l-14.63 14.63a79.56 79.56 0 0 1-56.13 23.43h-.45a79.52 79.52 0 0 1-55.89-22.77a8 8 0 1 0-11.18 11.44a95.36 95.36 0 0 0 67.07 27.33h.52a95.43 95.43 0 0 0 67.36-28.12L208 179.31V208a8 8 0 0 0 16 0v-48a8 8 0 0 0-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrows-out-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 48v160H48V48Z' opacity='.2'/%3E%3Cpath d='M216 48v48a8 8 0 0 1-16 0V67.31l-42.34 42.35a8 8 0 0 1-11.32-11.32L188.69 56H160a8 8 0 0 1 0-16h48a8 8 0 0 1 8 8M98.34 146.34L56 188.69V160a8 8 0 0 0-16 0v48a8 8 0 0 0 8 8h48a8 8 0 0 0 0-16H67.31l42.35-42.34a8 8 0 0 0-11.32-11.32M208 152a8 8 0 0 0-8 8v28.69l-42.34-42.35a8 8 0 0 0-11.32 11.32L188.69 200H160a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8M67.31 56H96a8 8 0 0 0 0-16H48a8 8 0 0 0-8 8v48a8 8 0 0 0 16 0V67.31l42.34 42.35a8 8 0 0 0 11.32-11.32Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-cards-three-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 104v96a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8v-96a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8' opacity='.2'/%3E%3Cpath d='M208 88H48a16 16 0 0 0-16 16v96a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-96a16 16 0 0 0-16-16m0 112H48v-96h160zM48 64a8 8 0 0 1 8-8h144a8 8 0 0 1 0 16H56a8 8 0 0 1-8-8m16-32a8 8 0 0 1 8-8h112a8 8 0 0 1 0 16H72a8 8 0 0 1-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-caret-down{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m213.66 101.66l-80 80a8 8 0 0 1-11.32 0l-80-80a8 8 0 0 1 11.32-11.32L128 164.69l74.34-74.35a8 8 0 0 1 11.32 11.32'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-caret-left{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M165.66 202.34a8 8 0 0 1-11.32 11.32l-80-80a8 8 0 0 1 0-11.32l80-80a8 8 0 0 1 11.32 11.32L91.31 128Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-caret-up{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M213.66 165.66a8 8 0 0 1-11.32 0L128 91.31l-74.34 74.35a8 8 0 0 1-11.32-11.32l80-80a8 8 0 0 1 11.32 0l80 80a8 8 0 0 1 0 11.32'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-check-bold{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m232.49 80.49l-128 128a12 12 0 0 1-17 0l-56-56a12 12 0 1 1 17-17L96 183L215.51 63.51a12 12 0 0 1 17 17Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-check-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M232 56v144a16 16 0 0 1-16 16H40a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16h176a16 16 0 0 1 16 16' opacity='.2'/%3E%3Cpath d='m205.66 85.66l-96 96a8 8 0 0 1-11.32 0l-40-40a8 8 0 0 1 11.32-11.32L104 164.69l90.34-90.35a8 8 0 0 1 11.32 11.32'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-circle-notch{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M232 128a104 104 0 0 1-208 0c0-41 23.81-78.36 60.66-95.27a8 8 0 0 1 6.68 14.54C60.15 61.59 40 93.27 40 128a88 88 0 0 0 176 0c0-34.73-20.15-66.41-51.34-80.73a8 8 0 0 1 6.68-14.54C208.19 49.64 232 87 232 128'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-eye-slash{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M53.92 34.62a8 8 0 1 0-11.84 10.76l19.24 21.17C25 88.84 9.38 123.2 8.69 124.76a8 8 0 0 0 0 6.5c.35.79 8.82 19.57 27.65 38.4C61.43 194.74 93.12 208 128 208a127.1 127.1 0 0 0 52.07-10.83l22 24.21a8 8 0 1 0 11.84-10.76Zm47.33 75.84l41.67 45.85a32 32 0 0 1-41.67-45.85M128 192c-30.78 0-57.67-11.19-79.93-33.25A133.2 133.2 0 0 1 25 128c4.69-8.79 19.66-33.39 47.35-49.38l18 19.75a48 48 0 0 0 63.66 70l14.73 16.2A112 112 0 0 1 128 192m6-95.43a8 8 0 0 1 3-15.72a48.16 48.16 0 0 1 38.77 42.64a8 8 0 0 1-7.22 8.71a6 6 0 0 1-.75 0a8 8 0 0 1-8-7.26A32.09 32.09 0 0 0 134 96.57m113.28 34.69c-.42.94-10.55 23.37-33.36 43.8a8 8 0 1 1-10.67-11.92a132.8 132.8 0 0 0 27.8-35.14a133.2 133.2 0 0 0-23.12-30.77C185.67 75.19 158.78 64 128 64a118.4 118.4 0 0 0-19.36 1.57A8 8 0 1 1 106 49.79A134 134 0 0 1 128 48c34.88 0 66.57 13.26 91.66 38.35c18.83 18.83 27.3 37.62 27.65 38.41a8 8 0 0 1 0 6.5Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-eye-slash-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M128 56c-80 0-112 72-112 72s32 72 112 72s112-72 112-72s-32-72-112-72m0 112a40 40 0 1 1 40-40a40 40 0 0 1-40 40' opacity='.2'/%3E%3Cpath d='M53.92 34.62a8 8 0 1 0-11.84 10.76l19.24 21.17C25 88.84 9.38 123.2 8.69 124.76a8 8 0 0 0 0 6.5c.35.79 8.82 19.57 27.65 38.4C61.43 194.74 93.12 208 128 208a127.1 127.1 0 0 0 52.07-10.83l22 24.21a8 8 0 1 0 11.84-10.76Zm47.33 75.84l41.67 45.85a32 32 0 0 1-41.67-45.85M128 192c-30.78 0-57.67-11.19-79.93-33.25A133.2 133.2 0 0 1 25 128c4.69-8.79 19.66-33.39 47.35-49.38l18 19.75a48 48 0 0 0 63.66 70l14.73 16.2A112 112 0 0 1 128 192m6-95.43a8 8 0 0 1 3-15.72a48.16 48.16 0 0 1 38.77 42.64a8 8 0 0 1-7.22 8.71a6 6 0 0 1-.75 0a8 8 0 0 1-8-7.26A32.09 32.09 0 0 0 134 96.57m113.28 34.69c-.42.94-10.55 23.37-33.36 43.8a8 8 0 1 1-10.67-11.92a132.8 132.8 0 0 0 27.8-35.14a133.2 133.2 0 0 0-23.12-30.77C185.67 75.19 158.78 64 128 64a118.4 118.4 0 0 0-19.36 1.57A8 8 0 1 1 106 49.79A134 134 0 0 1 128 48c34.88 0 66.57 13.26 91.66 38.35c18.83 18.83 27.3 37.62 27.65 38.41a8 8 0 0 1 0 6.5Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-gear-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='m207.86 123.18l16.78-21a99 99 0 0 0-10.07-24.29l-26.7-3a81 81 0 0 0-6.81-6.81l-3-26.71a99.4 99.4 0 0 0-24.3-10l-21 16.77a82 82 0 0 0-9.64 0l-21-16.78a99 99 0 0 0-24.21 10.07l-3 26.7a81 81 0 0 0-6.81 6.81l-26.71 3a99.4 99.4 0 0 0-10 24.3l16.77 21a82 82 0 0 0 0 9.64l-16.78 21a99 99 0 0 0 10.07 24.29l26.7 3a81 81 0 0 0 6.81 6.81l3 26.71a99.4 99.4 0 0 0 24.3 10l21-16.77a82 82 0 0 0 9.64 0l21 16.78a99 99 0 0 0 24.29-10.07l3-26.7a81 81 0 0 0 6.81-6.81l26.71-3a99.4 99.4 0 0 0 10-24.3l-16.77-21a82 82 0 0 0-.08-9.64M128 168a40 40 0 1 1 40-40a40 40 0 0 1-40 40' opacity='.2'/%3E%3Cpath d='M128 80a48 48 0 1 0 48 48a48.05 48.05 0 0 0-48-48m0 80a32 32 0 1 1 32-32a32 32 0 0 1-32 32m88-29.84q.06-2.16 0-4.32l14.92-18.64a8 8 0 0 0 1.48-7.06a107.6 107.6 0 0 0-10.88-26.25a8 8 0 0 0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186 40.54a8 8 0 0 0-3.94-6a107.3 107.3 0 0 0-26.25-10.86a8 8 0 0 0-7.06 1.48L130.16 40h-4.32L107.2 25.11a8 8 0 0 0-7.06-1.48a107.6 107.6 0 0 0-26.25 10.88a8 8 0 0 0-3.93 6l-2.64 23.76q-1.56 1.49-3 3L40.54 70a8 8 0 0 0-6 3.94a107.7 107.7 0 0 0-10.87 26.25a8 8 0 0 0 1.49 7.06L40 125.84v4.32L25.11 148.8a8 8 0 0 0-1.48 7.06a107.6 107.6 0 0 0 10.88 26.25a8 8 0 0 0 6 3.93l23.72 2.64q1.49 1.56 3 3L70 215.46a8 8 0 0 0 3.94 6a107.7 107.7 0 0 0 26.25 10.87a8 8 0 0 0 7.06-1.49L125.84 216q2.16.06 4.32 0l18.64 14.92a8 8 0 0 0 7.06 1.48a107.2 107.2 0 0 0 26.25-10.88a8 8 0 0 0 3.93-6l2.64-23.72q1.56-1.48 3-3l23.78-2.8a8 8 0 0 0 6-3.94a107.7 107.7 0 0 0 10.87-26.25a8 8 0 0 0-1.49-7.06Zm-16.1-6.5a74 74 0 0 1 0 8.68a8 8 0 0 0 1.74 5.48l14.19 17.73a91.6 91.6 0 0 1-6.23 15l-22.6 2.56a8 8 0 0 0-5.1 2.64a74 74 0 0 1-6.14 6.14a8 8 0 0 0-2.64 5.1l-2.51 22.58a91.3 91.3 0 0 1-15 6.23l-17.74-14.19a8 8 0 0 0-5-1.75h-.48a74 74 0 0 1-8.68 0a8.06 8.06 0 0 0-5.48 1.74l-17.78 14.2a91.6 91.6 0 0 1-15-6.23L82.89 187a8 8 0 0 0-2.64-5.1a74 74 0 0 1-6.14-6.14a8 8 0 0 0-5.1-2.64l-22.58-2.52a91.3 91.3 0 0 1-6.23-15l14.19-17.74a8 8 0 0 0 1.74-5.48a74 74 0 0 1 0-8.68a8 8 0 0 0-1.74-5.48L40.2 100.45a91.6 91.6 0 0 1 6.23-15L69 82.89a8 8 0 0 0 5.1-2.64a74 74 0 0 1 6.14-6.14A8 8 0 0 0 82.89 69l2.51-22.57a91.3 91.3 0 0 1 15-6.23l17.74 14.19a8 8 0 0 0 5.48 1.74a74 74 0 0 1 8.68 0a8.06 8.06 0 0 0 5.48-1.74l17.77-14.19a91.6 91.6 0 0 1 15 6.23L173.11 69a8 8 0 0 0 2.64 5.1a74 74 0 0 1 6.14 6.14a8 8 0 0 0 5.1 2.64l22.58 2.51a91.3 91.3 0 0 1 6.23 15l-14.19 17.74a8 8 0 0 0-1.74 5.53Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-globe{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M128 24a104 104 0 1 0 104 104A104.12 104.12 0 0 0 128 24m88 104a87.6 87.6 0 0 1-3.33 24h-38.51a157.4 157.4 0 0 0 0-48h38.51a87.6 87.6 0 0 1 3.33 24m-114 40h52a115.1 115.1 0 0 1-26 45a115.3 115.3 0 0 1-26-45m-3.9-16a140.8 140.8 0 0 1 0-48h59.88a140.8 140.8 0 0 1 0 48ZM40 128a87.6 87.6 0 0 1 3.33-24h38.51a157.4 157.4 0 0 0 0 48H43.33A87.6 87.6 0 0 1 40 128m114-40h-52a115.1 115.1 0 0 1 26-45a115.3 115.3 0 0 1 26 45m52.33 0h-35.62a135.3 135.3 0 0 0-22.3-45.6A88.29 88.29 0 0 1 206.37 88Zm-98.74-45.6A135.3 135.3 0 0 0 85.29 88H49.63a88.29 88.29 0 0 1 57.96-45.6M49.63 168h35.66a135.3 135.3 0 0 0 22.3 45.6A88.29 88.29 0 0 1 49.63 168m98.78 45.6a135.3 135.3 0 0 0 22.3-45.6h35.66a88.29 88.29 0 0 1-57.96 45.6'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-layout-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M104 104v104H40a8 8 0 0 1-8-8v-96Z' opacity='.2'/%3E%3Cpath d='M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m0 16v40H40V56ZM40 112h56v88H40Zm176 88H112v-88h104z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-push-pin{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m235.32 81.37l-60.69-60.68a16 16 0 0 0-22.63 0l-53.63 53.8c-10.66-3.34-35-7.37-60.4 13.14a16 16 0 0 0-1.29 23.78L85 159.71l-42.66 42.63a8 8 0 0 0 11.32 11.32L96.29 171l48.29 48.29A16 16 0 0 0 155.9 224h1.13a15.93 15.93 0 0 0 11.64-6.33c19.64-26.1 17.75-47.32 13.19-60L235.33 104a16 16 0 0 0-.01-22.63M224 92.69l-57.27 57.46a8 8 0 0 0-1.49 9.22c9.46 18.93-1.8 38.59-9.34 48.62L48 100.08c12.08-9.74 23.64-12.31 32.48-12.31A40.1 40.1 0 0 1 96.81 91a8 8 0 0 0 9.25-1.51L163.32 32L224 92.68Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-push-pin-fill{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m235.33 104l-53.47 53.65c4.56 12.67 6.45 33.89-13.19 60A15.93 15.93 0 0 1 157 224h-1.13a16 16 0 0 1-11.32-4.69L96.29 171l-42.63 42.66a8 8 0 0 1-11.32-11.32L85 159.71l-48.3-48.3A16 16 0 0 1 38 87.63c25.42-20.51 49.75-16.48 60.4-13.14L152 20.7a16 16 0 0 1 22.63 0l60.69 60.68a16 16 0 0 1 .01 22.62'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-rocket-launch-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M184 120v61.65a8 8 0 0 1-2.34 5.65l-34.35 34.35a8 8 0 0 1-13.57-4.53L128 176Zm-48-48H74.35a8 8 0 0 0-5.65 2.34l-34.35 34.35a8 8 0 0 0 4.53 13.57L80 128ZM40 216c37.65 0 50.69-19.69 54.56-28.18l-26.38-26.38C59.69 165.31 40 178.35 40 216' opacity='.2'/%3E%3Cpath d='M223.85 47.12a16 16 0 0 0-15-15c-12.58-.75-44.73.4-71.41 27.07L132.69 64H74.36A15.9 15.9 0 0 0 63 68.68L28.7 103a16 16 0 0 0 9.07 27.16l38.47 5.37l44.21 44.21l5.37 38.49a15.94 15.94 0 0 0 10.78 12.92a16.1 16.1 0 0 0 5.1.83a15.9 15.9 0 0 0 11.3-4.68l34.32-34.3a15.9 15.9 0 0 0 4.68-11.36v-58.33l4.77-4.77c26.68-26.68 27.83-58.83 27.08-71.42M74.36 80h42.33l-39.53 39.52L40 114.34Zm74.41-9.45a76.65 76.65 0 0 1 59.11-22.47a76.46 76.46 0 0 1-22.42 59.16L128 164.68L91.32 128ZM176 181.64L141.67 216l-5.19-37.17L176 139.31Zm-74.16 9.5C97.34 201 82.29 224 40 224a8 8 0 0 1-8-8c0-42.29 23-57.34 32.86-61.85a8 8 0 0 1 6.64 14.56c-6.43 2.93-20.62 12.36-23.12 38.91c26.55-2.5 36-16.69 38.91-23.12a8 8 0 1 1 14.56 6.64Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-square-half-bottom-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 128v72a8 8 0 0 1-8 8H56a8 8 0 0 1-8-8v-72Z' opacity='.2'/%3E%3Cpath d='M200 40H56a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m0 16v64H56V56Zm0 144H56v-64h144z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-trash-duotone,.i-ph\\:trash-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M200 56v152a8 8 0 0 1-8 8H64a8 8 0 0 1-8-8V56Z' opacity='.2'/%3E%3Cpath d='M216 48h-40v-8a24 24 0 0 0-24-24h-48a24 24 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16V64h8a8 8 0 0 0 0-16M96 40a8 8 0 0 1 8-8h48a8 8 0 0 1 8 8v8H96Zm96 168H64V64h128Zm-80-104v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0m48 0v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-warning-duotone,.i-ph\\:warning-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M215.46 216H40.54c-12.62 0-20.54-13.21-14.41-23.91l87.46-151.87c6.3-11 22.52-11 28.82 0l87.46 151.87c6.13 10.7-1.79 23.91-14.41 23.91' opacity='.2'/%3E%3Cpath d='M236.8 188.09L149.35 36.22a24.76 24.76 0 0 0-42.7 0L19.2 188.09a23.51 23.51 0 0 0 0 23.72A24.35 24.35 0 0 0 40.55 224h174.9a24.35 24.35 0 0 0 21.33-12.19a23.51 23.51 0 0 0 .02-23.72m-13.87 15.71a8.5 8.5 0 0 1-7.48 4.2H40.55a8.5 8.5 0 0 1-7.48-4.2a7.59 7.59 0 0 1 0-7.72l87.45-151.87a8.75 8.75 0 0 1 15 0l87.45 151.87a7.59 7.59 0 0 1-.04 7.72M120 144v-40a8 8 0 0 1 16 0v40a8 8 0 0 1-16 0m20 36a12 12 0 1 1-12-12a12 12 0 0 1 12 12'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-x{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M205.66 194.34a8 8 0 0 1-11.32 11.32L128 139.31l-66.34 66.35a8 8 0 0 1-11.32-11.32L116.69 128L50.34 61.66a8 8 0 0 1 11.32-11.32L128 116.69l66.34-66.35a8 8 0 0 1 11.32 11.32L139.31 128Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:bell-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 192H48a8 8 0 0 1-6.88-12C47.71 168.6 56 139.81 56 104a72 72 0 0 1 144 0c0 35.82 8.3 64.6 14.9 76a8 8 0 0 1-6.9 12' opacity='.2'/%3E%3Cpath d='M221.8 175.94c-5.55-9.56-13.8-36.61-13.8-71.94a80 80 0 1 0-160 0c0 35.34-8.26 62.38-13.81 71.94A16 16 0 0 0 48 200h40.81a40 40 0 0 0 78.38 0H208a16 16 0 0 0 13.8-24.06M128 216a24 24 0 0 1-22.62-16h45.24A24 24 0 0 1 128 216m-80-32c7.7-13.24 16-43.92 16-80a64 64 0 1 1 128 0c0 36.05 8.28 66.73 16 80Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:bell-slash-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 192H48a8 8 0 0 1-6.88-12C47.71 168.6 56 139.81 56 104a72 72 0 0 1 144 0c0 35.82 8.3 64.6 14.9 76a8 8 0 0 1-6.9 12' opacity='.2'/%3E%3Cpath d='M53.92 34.62a8 8 0 1 0-11.84 10.76L58.82 63.8A79.6 79.6 0 0 0 48 104c0 35.34-8.26 62.38-13.81 71.94A16 16 0 0 0 48 200h40.8a40 40 0 0 0 78.4 0h15.44l19.44 21.38a8 8 0 1 0 11.84-10.76ZM128 216a24 24 0 0 1-22.62-16h45.24A24 24 0 0 1 128 216m-80-32c7.7-13.24 16-43.92 16-80a63.65 63.65 0 0 1 6.26-27.62L168.09 184Zm166-4.73a8.1 8.1 0 0 1-2.93.55a8 8 0 0 1-7.44-5.08C196.35 156.19 192 129.75 192 104a64 64 0 0 0-95.57-55.69a8 8 0 0 1-7.9-13.91A80 80 0 0 1 208 104c0 35.35 8.05 58.59 10.52 64.88a8 8 0 0 1-4.52 10.37Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:bug-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 128v16a80 80 0 0 1-160 0v-16Z' opacity='.2'/%3E%3Cpath d='M144 92a12 12 0 1 1 12 12a12 12 0 0 1-12-12m-44-12a12 12 0 1 0 12 12a12 12 0 0 0-12-12m116 64a87.8 87.8 0 0 1-3 23l22.24 9.72A8 8 0 0 1 232 192a7.9 7.9 0 0 1-3.2-.67L207.38 182a88 88 0 0 1-158.76 0l-21.42 9.33a7.9 7.9 0 0 1-3.2.67a8 8 0 0 1-3.2-15.33L43 167a87.8 87.8 0 0 1-3-23v-8H16a8 8 0 0 1 0-16h24v-8a87.8 87.8 0 0 1 3-23l-22.2-9.67a8 8 0 1 1 6.4-14.66L48.62 74a88 88 0 0 1 158.76 0l21.42-9.36a8 8 0 0 1 6.4 14.66L213 89.05a87.8 87.8 0 0 1 3 23v8h24a8 8 0 0 1 0 16h-24ZM56 120h144v-8a72 72 0 0 0-144 0Zm64 95.54V136H56v8a72.08 72.08 0 0 0 64 71.54M200 144v-8h-64v79.54A72.08 72.08 0 0 0 200 144'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:check-circle-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M173.66 98.34a8 8 0 0 1 0 11.32l-56 56a8 8 0 0 1-11.32 0l-24-24a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 0M232 128A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:file-code-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 88h-56V32Z' opacity='.2'/%3E%3Cpath d='M181.66 146.34a8 8 0 0 1 0 11.32l-24 24a8 8 0 0 1-11.32-11.32L164.69 152l-18.35-18.34a8 8 0 0 1 11.32-11.32Zm-72-24a8 8 0 0 0-11.32 0l-24 24a8 8 0 0 0 0 11.32l24 24a8 8 0 0 0 11.32-11.32L91.31 152l18.35-18.34a8 8 0 0 0 0-11.32M216 88v128a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V40a16 16 0 0 1 16-16h96a8 8 0 0 1 5.66 2.34l56 56A8 8 0 0 1 216 88m-56-8h28.69L160 51.31Zm40 136V96h-48a8 8 0 0 1-8-8V40H56v176z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:funnel-x-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M221.9 61.38L152 136v58.65a8 8 0 0 1-3.56 6.66l-32 21.33A8 8 0 0 1 104 216v-80L34.1 61.38A8 8 0 0 1 40 48h176a8 8 0 0 1 5.9 13.38' opacity='.2'/%3E%3Cpath d='M227.82 66.76A16 16 0 0 0 216 40H40a16 16 0 0 0-11.81 26.76l.08.09L96 139.17V216a16 16 0 0 0 24.87 13.32l32-21.34a16 16 0 0 0 7.13-13.32v-55.49l67.73-72.32Zm-81.63 63.83A8 8 0 0 0 144 136v58.66L112 216v-80a8 8 0 0 0-2.16-5.46L40 56h176Zm99.49 79.81a8 8 0 0 1-11.32 11.32L216 203.32l-18.34 18.35a8 8 0 0 1-11.31-11.32L204.69 192l-18.34-18.35a8 8 0 0 1 11.31-11.31L216 180.69l18.34-18.34a8 8 0 0 1 11.32 11.31L227.31 192Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:globe-simple-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.12 104.12 0 0 0 128 24m87.62 96h-39.83c-1.79-36.51-15.85-62.33-27.38-77.6a88.19 88.19 0 0 1 67.22 77.6ZM96.23 136h63.54c-2.31 41.61-22.23 67.11-31.77 77c-9.55-9.9-29.46-35.4-31.77-77m0-16c2.31-41.61 22.23-67.11 31.77-77c9.55 9.93 29.46 35.43 31.77 77Zm11.36-77.6C96.06 57.67 82 83.49 80.21 120H40.37a88.19 88.19 0 0 1 67.22-77.6M40.37 136h39.84c1.82 36.51 15.85 62.33 27.38 77.6A88.19 88.19 0 0 1 40.37 136m108 77.6c11.53-15.27 25.56-41.09 27.38-77.6h39.84a88.19 88.19 0 0 1-67.18 77.6Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:hexagon-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 80.18v95.64a8 8 0 0 1-4.16 7l-88 48.18a8 8 0 0 1-7.68 0l-88-48.18a8 8 0 0 1-4.16-7V80.18a8 8 0 0 1 4.16-7l88-48.18a8 8 0 0 1 7.68 0l88 48.18a8 8 0 0 1 4.16 7' opacity='.2'/%3E%3Cpath d='m223.68 66.15l-88-48.15a15.88 15.88 0 0 0-15.36 0l-88 48.17a16 16 0 0 0-8.32 14v95.64a16 16 0 0 0 8.32 14l88 48.17a15.88 15.88 0 0 0 15.36 0l88-48.17a16 16 0 0 0 8.32-14V80.18a16 16 0 0 0-8.32-14.03M216 175.82L128 224l-88-48.18V80.18L128 32l88 48.17Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:info-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M144 176a8 8 0 0 1-8 8a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 8 8m88-48A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88m-92-32a12 12 0 1 0-12-12a12 12 0 0 0 12 12'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:sort-ascending-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 80v88l-24 24H48V64h160a16 16 0 0 1 16 16' opacity='.2'/%3E%3Cpath d='M128 128a8 8 0 0 1-8 8H48a8 8 0 0 1 0-16h72a8 8 0 0 1 8 8M48 72h136a8 8 0 0 0 0-16H48a8 8 0 0 0 0 16m56 112H48a8 8 0 0 0 0 16h56a8 8 0 0 0 0-16m125.66-21.66a8 8 0 0 0-11.32 0L192 188.69V112a8 8 0 0 0-16 0v76.69l-26.34-26.35a8 8 0 0 0-11.32 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:sort-descending-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 88v88a16 16 0 0 1-16 16H48V64h152Z' opacity='.2'/%3E%3Cpath d='M40 128a8 8 0 0 1 8-8h72a8 8 0 0 1 0 16H48a8 8 0 0 1-8-8m8-56h56a8 8 0 0 0 0-16H48a8 8 0 0 0 0 16m136 112H48a8 8 0 0 0 0 16h136a8 8 0 0 0 0-16m45.66-101.66l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 11.32 11.32L176 67.31V144a8 8 0 0 0 16 0V67.31l26.34 26.35a8 8 0 0 0 11.32-11.32'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:timer-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 136a88 88 0 1 1-88-88a88 88 0 0 1 88 88' opacity='.2'/%3E%3Cpath d='M128 40a96 96 0 1 0 96 96a96.11 96.11 0 0 0-96-96m0 176a80 80 0 1 1 80-80a80.09 80.09 0 0 1-80 80m45.66-125.66a8 8 0 0 1 0 11.32l-40 40a8 8 0 0 1-11.32-11.32l40-40a8 8 0 0 1 11.32 0M96 16a8 8 0 0 1 8-8h48a8 8 0 0 1 0 16h-48a8 8 0 0 1-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:warning-diamond-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='m229.67 133.62l-96 96a7.94 7.94 0 0 1-11.24 0l-96-96a7.94 7.94 0 0 1 0-11.24l96.05-96a7.94 7.94 0 0 1 11.24 0l96 96.05a7.94 7.94 0 0 1-.05 11.19' opacity='.2'/%3E%3Cpath d='M128 72a8 8 0 0 1 8 8v56a8 8 0 0 1-16 0V80a8 8 0 0 1 8-8m-12 100a12 12 0 1 0 12-12a12 12 0 0 0-12 12m124-44a15.85 15.85 0 0 1-4.67 11.28l-96.05 96.06a16 16 0 0 1-22.56 0l-96-96.06a16 16 0 0 1 0-22.56l96.05-96.06a16 16 0 0 1 22.56 0l96.05 96.06A15.85 15.85 0 0 1 240 128m-16 0l-96-96l-96 96l96 96Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:x-circle-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M165.66 101.66L139.31 128l26.35 26.34a8 8 0 0 1-11.32 11.32L128 139.31l-26.34 26.35a8 8 0 0 1-11.32-11.32L116.69 128l-26.35-26.34a8 8 0 0 1 11.32-11.32L128 116.69l26.34-26.35a8 8 0 0 1 11.32 11.32M232 128A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-svg-spinners-8-dots-rotate{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg%3E%3Ccircle cx='3' cy='12' r='2' fill='currentColor'/%3E%3Ccircle cx='21' cy='12' r='2' fill='currentColor'/%3E%3Ccircle cx='12' cy='21' r='2' fill='currentColor'/%3E%3Ccircle cx='12' cy='3' r='2' fill='currentColor'/%3E%3Ccircle cx='5.64' cy='5.64' r='2' fill='currentColor'/%3E%3Ccircle cx='18.36' cy='18.36' r='2' fill='currentColor'/%3E%3Ccircle cx='5.64' cy='18.36' r='2' fill='currentColor'/%3E%3Ccircle cx='18.36' cy='5.64' r='2' fill='currentColor'/%3E%3CanimateTransform attributeName='transform' dur='1.5s' repeatCount='indefinite' type='rotate' values='0 12 12;360 12 12'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.container{width:100%}.z-floating-anchor{z-index:2147483644}.z-floating-tooltip{z-index:2147483645}.border-base{--vdt-border-opacity:.13;border-color:rgba(136,136,136,var(--vdt-border-opacity))}.hover\\:border-base:hover{--vdt-border-opacity:.13;border-color:rgba(136,136,136,var(--vdt-border-opacity))}.bg-active{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.bg-base{--vdt-bg-opacity:1;background-color:rgba(255,255,255,var(--vdt-bg-opacity))}.bg-glass{--vdt-backdrop-blur:blur(7px);backdrop-filter:var(--vdt-backdrop-blur) var(--vdt-backdrop-brightness) var(--vdt-backdrop-contrast) var(--vdt-backdrop-grayscale) var(--vdt-backdrop-hue-rotate) var(--vdt-backdrop-invert) var(--vdt-backdrop-opacity) var(--vdt-backdrop-saturate) var(--vdt-backdrop-sepia);background-color:rgba(255,255,255,.5)}.bg-glass\\:75{--vdt-backdrop-blur:blur(7px);backdrop-filter:var(--vdt-backdrop-blur) var(--vdt-backdrop-brightness) var(--vdt-backdrop-contrast) var(--vdt-backdrop-grayscale) var(--vdt-backdrop-hue-rotate) var(--vdt-backdrop-invert) var(--vdt-backdrop-opacity) var(--vdt-backdrop-saturate) var(--vdt-backdrop-sepia);background-color:rgba(255,255,255,.75)}.hover\\:bg-active:hover{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.color-base{--vdt-text-opacity:1;color:rgba(38,38,38,var(--vdt-text-opacity))}@media (prefers-color-scheme:dark){.bg-base{--vdt-bg-opacity:1;background-color:rgba(17,17,17,var(--vdt-bg-opacity))}.bg-glass{background-color:rgba(17,17,17,.5)}.bg-glass\\:75{background-color:rgba(17,17,17,.75)}.color-base{--vdt-text-opacity:1;color:rgba(229,229,229,var(--vdt-text-opacity))}}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.disabled\\:pointer-events-none:disabled{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{top:0;bottom:0;left:0;right:0}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.bottom-4px{bottom:4px}.left--1{left:-.25rem}.left-0{left:0}.left-1\\/2{left:50%}.right--1{right:-.25rem}.right--1px{right:-1px}.right-0{right:0}.right-4{right:1rem}.top-0\\.5{top:.125rem}.top-1{top:.25rem}.top-1\\/2{top:50%}.top-4px{top:4px}.z--1{z-index:-1}.z-2147483647{z-index:2147483647}.grid{display:grid}.cols-\\[max-content_1fr\\]{grid-template-columns:max-content 1fr}.grid-cols-\\[1fr_1fr\\]{grid-template-columns:1fr 1fr}.grid-rows-\\[max-content_1fr\\]{grid-template-rows:max-content 1fr}.m-auto,.ma{margin:auto}.m1{margin:.25rem}.mx--1{margin-left:-.25rem;margin-right:-.25rem}.mx-0\\.5{margin-left:.125rem;margin-right:.125rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.mt-0\\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt8{margin-top:2rem}.box-border{box-sizing:border-box}.inline{display:inline}.hidden{display:none}.h-1\\.5{height:.375rem}.h-10{height:2.5rem}.h-20{height:5rem}.h-20px{height:20px}.h-3{height:.75rem}.h-3\\.5{height:.875rem}.h-4{height:1rem}.h-4\\.5{height:1.125rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-w-150{max-width:37.5rem}.max-w-200{max-width:50rem}.max-w-220px{max-width:220px}.min-h-0{min-height:0}.min-w-0{min-width:0}.min-w-28{min-width:7rem}.min-w-36{min-width:9rem}.w-\\[40px\\]{width:40px}.w-1\\.5{width:.375rem}.w-10{width:2.5rem}.w-2\\.5{width:.625rem}.w-20{width:5rem}.w-20px{width:20px}.w-2px{width:2px}.w-3{width:.75rem}.w-3\\.5{width:.875rem}.w-4{width:1rem}.w-4\\.5{width:1.125rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.w-px{width:1px}.w-screen{width:100vw}.flex{display:flex}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.shrink-0{flex-shrink:0}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.translate-x--1\\/2{--vdt-translate-x:-50%;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-x-1{--vdt-translate-x:.25rem;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-x-4{--vdt-translate-x:1rem;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-x-5{--vdt-translate-x:1.25rem;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-y--1\\/2{--vdt-translate-y:-50%;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate--45{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:-45deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-0{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:0deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-180{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:180deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-270{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:270deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-90{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:90deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.scale-120{--vdt-scale-x:1.2;--vdt-scale-y:1.2;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.hover\\:scale-110:hover{--vdt-scale-x:1.1;--vdt-scale-y:1.1;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.scale-y--100{--vdt-scale-y:-1;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.transform{transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.cursor-pointer{cursor:pointer}.select-none{user-select:none}.resize{resize:both}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.gap-0{gap:0}.gap-0\\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.of-auto,.overflow-auto{overflow:auto}.of-hidden,.overflow-hidden{overflow:hidden}.of-x-auto,.overflow-x-auto{overflow-x:auto}.of-y-auto{overflow-y:auto}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.whitespace-pre-wrap{white-space:pre-wrap}.ws-nowrap{white-space:nowrap}.border{border-width:1px}.border-1\\.5{border-width:1.5px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0}.border-b-1\\.5{border-bottom-width:1.5px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-r-1\\.5{border-right-width:1.5px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0}.border-amber\\/20{border-color:rgba(251,191,36,.2)}.border-current{border-color:currentColor}.border-transparent{border-color:transparent}.focus-within\\:border-gray\\/15:focus-within{border-color:rgba(156,163,175,.15)}.hover\\:border-gray\\/10:hover{border-color:rgba(156,163,175,.1)}.focus\\:border-purple:focus{--vdt-border-opacity:1;border-color:rgba(192,132,252,var(--vdt-border-opacity))}.border-t-transparent{border-top-color:transparent}.rounded{-webkit-border-radius:.25rem;border-radius:.25rem}.rounded-full{-webkit-border-radius:9999px;border-radius:9999px}.rounded-lg{-webkit-border-radius:.5rem;border-radius:.5rem}.rounded-md{-webkit-border-radius:.375rem;border-radius:.375rem}.rounded-xl{-webkit-border-radius:.75rem;border-radius:.75rem}.rounded-r{-webkit-border-top-right-radius:.25rem;border-top-right-radius:.25rem;-webkit-border-bottom-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-t{-webkit-border-top-left-radius:.25rem;border-top-left-radius:.25rem;-webkit-border-top-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-md{-webkit-border-top-left-radius:.375rem;border-top-left-radius:.375rem;-webkit-border-top-right-radius:.375rem;border-top-right-radius:.375rem}.bg-amber{--vdt-bg-opacity:1;background-color:rgba(251,191,36,var(--vdt-bg-opacity))}.bg-amber\\/10{background-color:rgba(251,191,36,.1)}.bg-black{--vdt-bg-opacity:1;background-color:rgba(0,0,0,var(--vdt-bg-opacity))}.bg-blue{--vdt-bg-opacity:1;background-color:rgba(96,165,250,var(--vdt-bg-opacity))}.bg-gray{--vdt-bg-opacity:1;background-color:rgba(156,163,175,var(--vdt-bg-opacity))}.bg-gray-6{--vdt-bg-opacity:1;background-color:rgba(75,85,99,var(--vdt-bg-opacity))}.bg-gray\\/10{background-color:rgba(156,163,175,.1)}.bg-gray\\/20{background-color:rgba(156,163,175,.2)}.bg-gray\\/30{background-color:rgba(156,163,175,.3)}.bg-gray\\/5{background-color:rgba(156,163,175,.05)}.bg-green{--vdt-bg-opacity:1;background-color:rgba(74,222,128,var(--vdt-bg-opacity))}.bg-green\\:5{background-color:rgba(74,222,128,.05)}.bg-lime{--vdt-bg-opacity:1;background-color:rgba(163,230,53,var(--vdt-bg-opacity))}.bg-lime\\/20{background-color:rgba(163,230,53,.2)}.bg-lime6{--vdt-bg-opacity:1;background-color:rgba(101,163,13,var(--vdt-bg-opacity))}.bg-red{--vdt-bg-opacity:1;background-color:rgba(248,113,113,var(--vdt-bg-opacity))}.bg-red\\/10{background-color:rgba(248,113,113,.1)}.bg-transparent{background-color:transparent}.bg-white{--vdt-bg-opacity:1;background-color:rgba(255,255,255,var(--vdt-bg-opacity))}.hover\\:bg-\\[\\#8881\\]:hover{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.hover\\:bg-gray\\/10:hover{background-color:rgba(156,163,175,.1)}.hover\\:bg-gray\\/15:hover{background-color:rgba(156,163,175,.15)}.hover\\:bg-gray\\/20:hover{background-color:rgba(156,163,175,.2)}.hover\\:bg-gray\\/5:hover{background-color:rgba(156,163,175,.05)}.hover\\:bg-lime7:hover{--vdt-bg-opacity:1;background-color:rgba(77,124,15,var(--vdt-bg-opacity))}.hover\\:bg-red\\/20:hover{background-color:rgba(248,113,113,.2)}.disabled\\:bg-gray6\\!:disabled{--vdt-bg-opacity:1!important;background-color:rgba(75,85,99,var(--vdt-bg-opacity))!important}.fill-black{--vdt-fill-opacity:1;fill:rgba(0,0,0,var(--vdt-fill-opacity))}.fill-hex-08060D{--vdt-fill-opacity:1;fill:rgba(8,6,13,var(--vdt-fill-opacity))}.p-0\\.5{padding:.125rem}.p-1,.p1{padding:.25rem}.p-2,.p2{padding:.5rem}.p-4{padding:1rem}.p1\\.5{padding:.375rem}.p10{padding:2.5rem}.p20{padding:5rem}.p3{padding:.75rem}.px,.px-4,.px4{padding-left:1rem;padding-right:1rem}.px-1,.px1{padding-left:.25rem;padding-right:.25rem}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2,.px2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3,.px3{padding-left:.75rem;padding-right:.75rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py1{padding-top:.25rem;padding-bottom:.25rem}.py1\\.5{padding-top:.375rem;padding-bottom:.375rem}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.text-left{text-align:left}.text-\\[15px\\]{font-size:15px}.text-0\\.6em{font-size:.6em}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.text-amber{--vdt-text-opacity:1;color:rgba(251,191,36,var(--vdt-text-opacity))}.text-amber-800{--vdt-text-opacity:1;color:rgba(146,64,14,var(--vdt-text-opacity))}.text-blue{--vdt-text-opacity:1;color:rgba(96,165,250,var(--vdt-text-opacity))}.text-gray{--vdt-text-opacity:1;color:rgba(156,163,175,var(--vdt-text-opacity))}.text-green{--vdt-text-opacity:1;color:rgba(74,222,128,var(--vdt-text-opacity))}.text-green-800{--vdt-text-opacity:1;color:rgba(22,101,52,var(--vdt-text-opacity))}.text-lime{--vdt-text-opacity:1;color:rgba(163,230,53,var(--vdt-text-opacity))}.text-orange{--vdt-text-opacity:1;color:rgba(251,146,60,var(--vdt-text-opacity))}.text-primary{--vdt-text-opacity:1;color:rgba(213,119,255,var(--vdt-text-opacity))}.text-purple{--vdt-text-opacity:1;color:rgba(192,132,252,var(--vdt-text-opacity))}.text-red{--vdt-text-opacity:1;color:rgba(248,113,113,var(--vdt-text-opacity))}.text-violet{--vdt-text-opacity:1;color:rgba(167,139,250,var(--vdt-text-opacity))}.text-white{--vdt-text-opacity:1;color:rgba(255,255,255,var(--vdt-text-opacity))}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.capitalize{text-transform:capitalize}.line-through{text-decoration-line:line-through}.hover\\:underline:hover{text-decoration-line:underline}.tab{tab-size:4}.op0,.opacity-0{opacity:0}.op100,.opacity-100{opacity:1}.group:hover .group-hover\\:opacity-100{opacity:1}.op30{opacity:.3}.op40{opacity:.4}.op50{opacity:.5}.group:hover .group-hover\\:op50{opacity:.5}.op60{opacity:.6}.op60\\!{opacity:.6!important}.op70{opacity:.7}.op75{opacity:.75}.op80{opacity:.8}.op85{opacity:.85}.hover\\:op100:hover{opacity:1}.hover\\:op100\\!:hover{opacity:1!important}.hover\\:op70:hover{opacity:.7}.shadow{--vdt-shadow:var(--vdt-shadow-inset) 0 1px 3px 0 var(--vdt-shadow-color,rgba(0,0,0,.1)),var(--vdt-shadow-inset) 0 1px 2px -1px var(--vdt-shadow-color,rgba(0,0,0,.1));box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.shadow-xl{--vdt-shadow:var(--vdt-shadow-inset) 0 20px 25px -5px var(--vdt-shadow-color,rgba(0,0,0,.1)),var(--vdt-shadow-inset) 0 8px 10px -6px var(--vdt-shadow-color,rgba(0,0,0,.1));box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.outline-none{outline-offset:2px;outline:2px solid transparent}.saturate-0{--vdt-saturate:saturate(0);filter:var(--vdt-blur) var(--vdt-brightness) var(--vdt-contrast) var(--vdt-drop-shadow) var(--vdt-grayscale) var(--vdt-hue-rotate) var(--vdt-invert) var(--vdt-saturate) var(--vdt-sepia)}.filter{filter:var(--vdt-blur) var(--vdt-brightness) var(--vdt-contrast) var(--vdt-drop-shadow) var(--vdt-grayscale) var(--vdt-hue-rotate) var(--vdt-invert) var(--vdt-saturate) var(--vdt-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.delay-200{transition-delay:.2s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@media (prefers-color-scheme:dark){.dark-hidden{display:none}.dark\\:fill-hex-fff,.dark\\:fill-white{--vdt-fill-opacity:1;fill:rgba(255,255,255,var(--vdt-fill-opacity))}.dark\\:text-amber-200{--vdt-text-opacity:1;color:rgba(253,230,138,var(--vdt-text-opacity))}.dark\\:text-green-200{--vdt-text-opacity:1;color:rgba(187,247,208,var(--vdt-text-opacity))}}@media (prefers-color-scheme:light){.light-hidden{display:none}}" +export default "*{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:before{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:after{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:before{--un-content:\"\"}:after{--un-content:\"\"}html{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button{-webkit-appearance:button;background-color:transparent;background-image:none}[type=button]{-webkit-appearance:button;background-color:transparent;background-image:none}[type=reset]{-webkit-appearance:button;background-color:transparent;background-image:none}[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::placeholder{opacity:1;color:#9ca3af}textarea::placeholder{opacity:1;color:#9ca3af}button{cursor:pointer}[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.xterm{cursor:text;user-select:none;position:relative}.xterm.focus{outline:none}.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;top:0;bottom:0;left:0;right:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-message{z-index:10;color:transparent;pointer-events:none;position:absolute;top:0;bottom:0;left:0;right:0}.xterm .xterm-accessibility:not(.debug){z-index:10;color:transparent;pointer-events:none;position:absolute;top:0;bottom:0;left:0;right:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre;font-family:monospace}.xterm .xterm-accessibility-tree>div{transform-origin:0;width:fit-content}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:underline double}.xterm-underline-3{text-decoration:underline wavy}.xterm-underline-4{text-decoration:underline dotted}.xterm-underline-5{text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;z-index:11;background:0 0;transition:opacity .1s linear}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{display:none;position:absolute}.xterm .xterm-scrollable-element>.shadow.top{width:100%;height:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;display:block;top:0;left:3px}.xterm .xterm-scrollable-element>.shadow.left{width:3px;height:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;display:block;top:3px;left:0}.xterm .xterm-scrollable-element>.shadow.top-left-corner{width:3px;height:3px;display:block;top:0;left:0}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset}:root{--un-text-opacity:100%}#vite-devtools-anchor{z-index:2147483644;box-sizing:border-box;transform-origin:50%;width:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:15px;position:fixed;transform:translate(-50%,-50%)rotate(0)}#vite-devtools-anchor #vite-devtools-dock-container{width:max-content;min-width:100px;height:40px;display:flex;position:absolute;top:0;left:0;transform:translate(-50%,-50%)}#vite-devtools-anchor.vite-devtools-vertical #vite-devtools-dock-container{transition-property:all;transition-duration:.5s;transition-timing-function:cubic-bezier(.4,0,.2,1);transform:translate(-50%,-50%)rotate(90deg)}#vite-devtools-anchor #vite-devtools-dock{touch-action:none;user-select:none;--vdt-backdrop-blur:blur(7px);height:40px;backdrop-filter:var(--vdt-backdrop-blur) var(--vdt-backdrop-brightness) var(--vdt-backdrop-contrast) var(--vdt-backdrop-grayscale) var(--vdt-backdrop-hue-rotate) var(--vdt-backdrop-invert) var(--vdt-backdrop-opacity) var(--vdt-backdrop-saturate) var(--vdt-backdrop-sepia);--vdt-text-opacity:1;color:rgba(51,51,51,var(--vdt-text-opacity));--vdt-shadow:var(--vdt-shadow-inset) 0 1px 3px 0 var(--vdt-shadow-color,rgba(0,0,0,.1)),var(--vdt-shadow-inset) 0 1px 2px -1px var(--vdt-shadow-color,rgba(0,0,0,.1));box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow);width:calc-size(max-content, size);background-color:rgba(255,255,255,.5);-webkit-border-radius:9999px;border-radius:9999px;margin:auto;transition-property:all;transition-duration:.5s;transition-timing-function:cubic-bezier(.4,0,.2,1)}@media (prefers-color-scheme:dark){#vite-devtools-anchor #vite-devtools-dock{--vdt-text-opacity:1;color:rgba(255,255,255,var(--vdt-text-opacity));background-color:rgba(17,17,17,.5)}}#vite-devtools-anchor.vite-devtools-minimized #vite-devtools-dock{width:22px;height:22px;padding:2px 0}#vite-devtools-anchor.vite-devtools-minimized .vite-devtools-dock-bracket{opacity:.5;width:.375rem}#vite-devtools-anchor:hover #vite-devtools-glowing{opacity:.6}#vite-devtools-anchor #vite-devtools-glowing{pointer-events:none;z-index:-1;opacity:0;--vdt-blur:blur(60px);width:160px;height:160px;filter:var(--vdt-blur) var(--vdt-brightness) var(--vdt-contrast) var(--vdt-drop-shadow) var(--vdt-grayscale) var(--vdt-hue-rotate) var(--vdt-invert) var(--vdt-saturate) var(--vdt-sepia);background-image:linear-gradient(45deg,#61d9ff,#7a23a1,#715ebd);-webkit-border-radius:9999px;border-radius:9999px;transition-property:all;transition-duration:1s;transition-timing-function:cubic-bezier(0,0,.2,1);position:absolute;top:0;left:0;transform:translate(-50%,-50%)}@media print{#vite-devtools-anchor{display:none}}.vite-devtools-resize-handle-horizontal{cursor:ns-resize;-webkit-border-radius:.375rem;border-radius:.375rem;height:10px;margin-top:-5px;margin-bottom:-5px;position:absolute;left:6px;right:6px}.vite-devtools-resize-handle-vertical{cursor:ew-resize;-webkit-border-radius:.375rem;border-radius:.375rem;width:10px;margin-left:-5px;margin-right:-5px;position:absolute;top:6px;bottom:0}.vite-devtools-resize-handle-corner{-webkit-border-radius:.375rem;border-radius:.375rem;width:14px;height:14px;margin:-6px;position:absolute}.vite-devtools-resize-handle{z-index:30}.vite-devtools-resize-handle:hover{background-color:rgba(156,163,175,.1)}*{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }:before{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }:after{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }::backdrop{--vdt-rotate:0;--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-scale-x:1;--vdt-scale-y:1;--vdt-scale-z:1;--vdt-skew-x:0;--vdt-skew-y:0;--vdt-translate-x:0;--vdt-translate-y:0;--vdt-translate-z:0;--vdt-pan-x: ;--vdt-pan-y: ;--vdt-pinch-zoom: ;--vdt-scroll-snap-strictness:proximity;--vdt-ordinal: ;--vdt-slashed-zero: ;--vdt-numeric-figure: ;--vdt-numeric-spacing: ;--vdt-numeric-fraction: ;--vdt-border-spacing-x:0;--vdt-border-spacing-y:0;--vdt-ring-offset-shadow:0 0 transparent;--vdt-ring-shadow:0 0 transparent;--vdt-shadow-inset: ;--vdt-shadow:0 0 transparent;--vdt-ring-inset: ;--vdt-ring-offset-width:0px;--vdt-ring-offset-color:#fff;--vdt-ring-width:0px;--vdt-ring-color:rgba(147,197,253,.5);--vdt-blur: ;--vdt-brightness: ;--vdt-contrast: ;--vdt-drop-shadow: ;--vdt-grayscale: ;--vdt-hue-rotate: ;--vdt-invert: ;--vdt-saturate: ;--vdt-sepia: ;--vdt-backdrop-blur: ;--vdt-backdrop-brightness: ;--vdt-backdrop-contrast: ;--vdt-backdrop-grayscale: ;--vdt-backdrop-hue-rotate: ;--vdt-backdrop-invert: ;--vdt-backdrop-opacity: ;--vdt-backdrop-saturate: ;--vdt-backdrop-sepia: }.i-carbon-clean{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z'/%3E%3Cpath fill='currentColor' d='M17.003 20a4.9 4.9 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.7 5.7 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2m-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848M15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.6 16.6 0 0 1 10 24H8a17.3 17.3 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13 13 0 0 0 17.596 28Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-fluent-emoji-flat-warning{background:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='none'%3E%3Cpath fill='%23FFB02E' d='m14.839 5.668l-12.66 21.93c-.51.89.13 2.01 1.16 2.01h25.32c1.03 0 1.67-1.11 1.16-2.01l-12.66-21.93c-.52-.89-1.8-.89-2.32 0'/%3E%3Cpath fill='%23000' d='M14.599 21.498a1.4 1.4 0 1 0 2.8-.01v-9.16c0-.77-.62-1.4-1.4-1.4c-.77 0-1.4.62-1.4 1.4zm2.8 3.98a1.4 1.4 0 1 1-2.8 0a1.4 1.4 0 0 1 2.8 0'/%3E%3C/g%3E%3C/svg%3E\") 0 0/100% 100% no-repeat;width:1em;height:1em}.i-ph-arrow-clockwise{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M240 56v48a8 8 0 0 1-8 8h-48a8 8 0 0 1 0-16h27.4l-26.59-24.36l-.25-.24a80 80 0 1 0-1.67 114.78a8 8 0 0 1 11 11.63A95.44 95.44 0 0 1 128 224h-1.32a96 96 0 1 1 69.07-164L224 85.8V56a8 8 0 1 1 16 0'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrow-clockwise-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 128a88 88 0 1 1-88-88a88 88 0 0 1 88 88' opacity='.2'/%3E%3Cpath d='M240 56v48a8 8 0 0 1-8 8h-48a8 8 0 0 1 0-16h27.4l-26.59-24.36l-.25-.24a80 80 0 1 0-1.67 114.78a8 8 0 0 1 11 11.63A95.44 95.44 0 0 1 128 224h-1.32a96 96 0 1 1 69.07-164L224 85.8V56a8 8 0 1 1 16 0'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrow-counter-clockwise{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M224 128a96 96 0 0 1-94.71 96H128a95.38 95.38 0 0 1-65.9-26.2a8 8 0 0 1 11-11.63a80 80 0 1 0-1.67-114.78a3 3 0 0 1-.26.25L44.59 96H72a8 8 0 0 1 0 16H24a8 8 0 0 1-8-8V56a8 8 0 0 1 16 0v29.8L60.25 60A96 96 0 0 1 224 128'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrow-square-out-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M184 80v128a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8V80a8 8 0 0 1 8-8h128a8 8 0 0 1 8 8' opacity='.2'/%3E%3Cpath d='M224 104a8 8 0 0 1-16 0V59.32l-66.33 66.34a8 8 0 0 1-11.32-11.32L196.68 48H152a8 8 0 0 1 0-16h64a8 8 0 0 1 8 8Zm-40 24a8 8 0 0 0-8 8v72H48V80h72a8 8 0 0 0 0-16H48a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-72a8 8 0 0 0-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrows-counter-clockwise-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 128a88 88 0 1 1-88-88a88 88 0 0 1 88 88' opacity='.2'/%3E%3Cpath d='M88 104H40a8 8 0 0 1-8-8V48a8 8 0 0 1 16 0v28.69l14.63-14.63A95.43 95.43 0 0 1 130 33.94h.53a95.36 95.36 0 0 1 67.07 27.33a8 8 0 0 1-11.18 11.44a79.52 79.52 0 0 0-55.89-22.77h-.45a79.56 79.56 0 0 0-56.14 23.43L59.31 88H88a8 8 0 0 1 0 16m128 48h-48a8 8 0 0 0 0 16h28.69l-14.63 14.63a79.56 79.56 0 0 1-56.13 23.43h-.45a79.52 79.52 0 0 1-55.89-22.77a8 8 0 1 0-11.18 11.44a95.36 95.36 0 0 0 67.07 27.33h.52a95.43 95.43 0 0 0 67.36-28.12L208 179.31V208a8 8 0 0 0 16 0v-48a8 8 0 0 0-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-arrows-out-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 48v160H48V48Z' opacity='.2'/%3E%3Cpath d='M216 48v48a8 8 0 0 1-16 0V67.31l-42.34 42.35a8 8 0 0 1-11.32-11.32L188.69 56H160a8 8 0 0 1 0-16h48a8 8 0 0 1 8 8M98.34 146.34L56 188.69V160a8 8 0 0 0-16 0v48a8 8 0 0 0 8 8h48a8 8 0 0 0 0-16H67.31l42.35-42.34a8 8 0 0 0-11.32-11.32M208 152a8 8 0 0 0-8 8v28.69l-42.34-42.35a8 8 0 0 0-11.32 11.32L188.69 200H160a8 8 0 0 0 0 16h48a8 8 0 0 0 8-8v-48a8 8 0 0 0-8-8M67.31 56H96a8 8 0 0 0 0-16H48a8 8 0 0 0-8 8v48a8 8 0 0 0 16 0V67.31l42.34 42.35a8 8 0 0 0 11.32-11.32Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-cards-three-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 104v96a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8v-96a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8' opacity='.2'/%3E%3Cpath d='M208 88H48a16 16 0 0 0-16 16v96a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-96a16 16 0 0 0-16-16m0 112H48v-96h160zM48 64a8 8 0 0 1 8-8h144a8 8 0 0 1 0 16H56a8 8 0 0 1-8-8m16-32a8 8 0 0 1 8-8h112a8 8 0 0 1 0 16H72a8 8 0 0 1-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-caret-down{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m213.66 101.66l-80 80a8 8 0 0 1-11.32 0l-80-80a8 8 0 0 1 11.32-11.32L128 164.69l74.34-74.35a8 8 0 0 1 11.32 11.32'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-caret-left{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M165.66 202.34a8 8 0 0 1-11.32 11.32l-80-80a8 8 0 0 1 0-11.32l80-80a8 8 0 0 1 11.32 11.32L91.31 128Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-caret-up{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M213.66 165.66a8 8 0 0 1-11.32 0L128 91.31l-74.34 74.35a8 8 0 0 1-11.32-11.32l80-80a8 8 0 0 1 11.32 0l80 80a8 8 0 0 1 0 11.32'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-check-bold{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m232.49 80.49l-128 128a12 12 0 0 1-17 0l-56-56a12 12 0 1 1 17-17L96 183L215.51 63.51a12 12 0 0 1 17 17Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-check-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M232 56v144a16 16 0 0 1-16 16H40a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16h176a16 16 0 0 1 16 16' opacity='.2'/%3E%3Cpath d='m205.66 85.66l-96 96a8 8 0 0 1-11.32 0l-40-40a8 8 0 0 1 11.32-11.32L104 164.69l90.34-90.35a8 8 0 0 1 11.32 11.32'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-circle-notch{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M232 128a104 104 0 0 1-208 0c0-41 23.81-78.36 60.66-95.27a8 8 0 0 1 6.68 14.54C60.15 61.59 40 93.27 40 128a88 88 0 0 0 176 0c0-34.73-20.15-66.41-51.34-80.73a8 8 0 0 1 6.68-14.54C208.19 49.64 232 87 232 128'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-eye-slash{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M53.92 34.62a8 8 0 1 0-11.84 10.76l19.24 21.17C25 88.84 9.38 123.2 8.69 124.76a8 8 0 0 0 0 6.5c.35.79 8.82 19.57 27.65 38.4C61.43 194.74 93.12 208 128 208a127.1 127.1 0 0 0 52.07-10.83l22 24.21a8 8 0 1 0 11.84-10.76Zm47.33 75.84l41.67 45.85a32 32 0 0 1-41.67-45.85M128 192c-30.78 0-57.67-11.19-79.93-33.25A133.2 133.2 0 0 1 25 128c4.69-8.79 19.66-33.39 47.35-49.38l18 19.75a48 48 0 0 0 63.66 70l14.73 16.2A112 112 0 0 1 128 192m6-95.43a8 8 0 0 1 3-15.72a48.16 48.16 0 0 1 38.77 42.64a8 8 0 0 1-7.22 8.71a6 6 0 0 1-.75 0a8 8 0 0 1-8-7.26A32.09 32.09 0 0 0 134 96.57m113.28 34.69c-.42.94-10.55 23.37-33.36 43.8a8 8 0 1 1-10.67-11.92a132.8 132.8 0 0 0 27.8-35.14a133.2 133.2 0 0 0-23.12-30.77C185.67 75.19 158.78 64 128 64a118.4 118.4 0 0 0-19.36 1.57A8 8 0 1 1 106 49.79A134 134 0 0 1 128 48c34.88 0 66.57 13.26 91.66 38.35c18.83 18.83 27.3 37.62 27.65 38.41a8 8 0 0 1 0 6.5Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-eye-slash-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M128 56c-80 0-112 72-112 72s32 72 112 72s112-72 112-72s-32-72-112-72m0 112a40 40 0 1 1 40-40a40 40 0 0 1-40 40' opacity='.2'/%3E%3Cpath d='M53.92 34.62a8 8 0 1 0-11.84 10.76l19.24 21.17C25 88.84 9.38 123.2 8.69 124.76a8 8 0 0 0 0 6.5c.35.79 8.82 19.57 27.65 38.4C61.43 194.74 93.12 208 128 208a127.1 127.1 0 0 0 52.07-10.83l22 24.21a8 8 0 1 0 11.84-10.76Zm47.33 75.84l41.67 45.85a32 32 0 0 1-41.67-45.85M128 192c-30.78 0-57.67-11.19-79.93-33.25A133.2 133.2 0 0 1 25 128c4.69-8.79 19.66-33.39 47.35-49.38l18 19.75a48 48 0 0 0 63.66 70l14.73 16.2A112 112 0 0 1 128 192m6-95.43a8 8 0 0 1 3-15.72a48.16 48.16 0 0 1 38.77 42.64a8 8 0 0 1-7.22 8.71a6 6 0 0 1-.75 0a8 8 0 0 1-8-7.26A32.09 32.09 0 0 0 134 96.57m113.28 34.69c-.42.94-10.55 23.37-33.36 43.8a8 8 0 1 1-10.67-11.92a132.8 132.8 0 0 0 27.8-35.14a133.2 133.2 0 0 0-23.12-30.77C185.67 75.19 158.78 64 128 64a118.4 118.4 0 0 0-19.36 1.57A8 8 0 1 1 106 49.79A134 134 0 0 1 128 48c34.88 0 66.57 13.26 91.66 38.35c18.83 18.83 27.3 37.62 27.65 38.41a8 8 0 0 1 0 6.5Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-gear-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='m207.86 123.18l16.78-21a99 99 0 0 0-10.07-24.29l-26.7-3a81 81 0 0 0-6.81-6.81l-3-26.71a99.4 99.4 0 0 0-24.3-10l-21 16.77a82 82 0 0 0-9.64 0l-21-16.78a99 99 0 0 0-24.21 10.07l-3 26.7a81 81 0 0 0-6.81 6.81l-26.71 3a99.4 99.4 0 0 0-10 24.3l16.77 21a82 82 0 0 0 0 9.64l-16.78 21a99 99 0 0 0 10.07 24.29l26.7 3a81 81 0 0 0 6.81 6.81l3 26.71a99.4 99.4 0 0 0 24.3 10l21-16.77a82 82 0 0 0 9.64 0l21 16.78a99 99 0 0 0 24.29-10.07l3-26.7a81 81 0 0 0 6.81-6.81l26.71-3a99.4 99.4 0 0 0 10-24.3l-16.77-21a82 82 0 0 0-.08-9.64M128 168a40 40 0 1 1 40-40a40 40 0 0 1-40 40' opacity='.2'/%3E%3Cpath d='M128 80a48 48 0 1 0 48 48a48.05 48.05 0 0 0-48-48m0 80a32 32 0 1 1 32-32a32 32 0 0 1-32 32m88-29.84q.06-2.16 0-4.32l14.92-18.64a8 8 0 0 0 1.48-7.06a107.6 107.6 0 0 0-10.88-26.25a8 8 0 0 0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186 40.54a8 8 0 0 0-3.94-6a107.3 107.3 0 0 0-26.25-10.86a8 8 0 0 0-7.06 1.48L130.16 40h-4.32L107.2 25.11a8 8 0 0 0-7.06-1.48a107.6 107.6 0 0 0-26.25 10.88a8 8 0 0 0-3.93 6l-2.64 23.76q-1.56 1.49-3 3L40.54 70a8 8 0 0 0-6 3.94a107.7 107.7 0 0 0-10.87 26.25a8 8 0 0 0 1.49 7.06L40 125.84v4.32L25.11 148.8a8 8 0 0 0-1.48 7.06a107.6 107.6 0 0 0 10.88 26.25a8 8 0 0 0 6 3.93l23.72 2.64q1.49 1.56 3 3L70 215.46a8 8 0 0 0 3.94 6a107.7 107.7 0 0 0 26.25 10.87a8 8 0 0 0 7.06-1.49L125.84 216q2.16.06 4.32 0l18.64 14.92a8 8 0 0 0 7.06 1.48a107.2 107.2 0 0 0 26.25-10.88a8 8 0 0 0 3.93-6l2.64-23.72q1.56-1.48 3-3l23.78-2.8a8 8 0 0 0 6-3.94a107.7 107.7 0 0 0 10.87-26.25a8 8 0 0 0-1.49-7.06Zm-16.1-6.5a74 74 0 0 1 0 8.68a8 8 0 0 0 1.74 5.48l14.19 17.73a91.6 91.6 0 0 1-6.23 15l-22.6 2.56a8 8 0 0 0-5.1 2.64a74 74 0 0 1-6.14 6.14a8 8 0 0 0-2.64 5.1l-2.51 22.58a91.3 91.3 0 0 1-15 6.23l-17.74-14.19a8 8 0 0 0-5-1.75h-.48a74 74 0 0 1-8.68 0a8.06 8.06 0 0 0-5.48 1.74l-17.78 14.2a91.6 91.6 0 0 1-15-6.23L82.89 187a8 8 0 0 0-2.64-5.1a74 74 0 0 1-6.14-6.14a8 8 0 0 0-5.1-2.64l-22.58-2.52a91.3 91.3 0 0 1-6.23-15l14.19-17.74a8 8 0 0 0 1.74-5.48a74 74 0 0 1 0-8.68a8 8 0 0 0-1.74-5.48L40.2 100.45a91.6 91.6 0 0 1 6.23-15L69 82.89a8 8 0 0 0 5.1-2.64a74 74 0 0 1 6.14-6.14A8 8 0 0 0 82.89 69l2.51-22.57a91.3 91.3 0 0 1 15-6.23l17.74 14.19a8 8 0 0 0 5.48 1.74a74 74 0 0 1 8.68 0a8.06 8.06 0 0 0 5.48-1.74l17.77-14.19a91.6 91.6 0 0 1 15 6.23L173.11 69a8 8 0 0 0 2.64 5.1a74 74 0 0 1 6.14 6.14a8 8 0 0 0 5.1 2.64l22.58 2.51a91.3 91.3 0 0 1 6.23 15l-14.19 17.74a8 8 0 0 0-1.74 5.53Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-globe{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M128 24a104 104 0 1 0 104 104A104.12 104.12 0 0 0 128 24m88 104a87.6 87.6 0 0 1-3.33 24h-38.51a157.4 157.4 0 0 0 0-48h38.51a87.6 87.6 0 0 1 3.33 24m-114 40h52a115.1 115.1 0 0 1-26 45a115.3 115.3 0 0 1-26-45m-3.9-16a140.8 140.8 0 0 1 0-48h59.88a140.8 140.8 0 0 1 0 48ZM40 128a87.6 87.6 0 0 1 3.33-24h38.51a157.4 157.4 0 0 0 0 48H43.33A87.6 87.6 0 0 1 40 128m114-40h-52a115.1 115.1 0 0 1 26-45a115.3 115.3 0 0 1 26 45m52.33 0h-35.62a135.3 135.3 0 0 0-22.3-45.6A88.29 88.29 0 0 1 206.37 88Zm-98.74-45.6A135.3 135.3 0 0 0 85.29 88H49.63a88.29 88.29 0 0 1 57.96-45.6M49.63 168h35.66a135.3 135.3 0 0 0 22.3 45.6A88.29 88.29 0 0 1 49.63 168m98.78 45.6a135.3 135.3 0 0 0 22.3-45.6h35.66a88.29 88.29 0 0 1-57.96 45.6'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-layout-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M104 104v104H40a8 8 0 0 1-8-8v-96Z' opacity='.2'/%3E%3Cpath d='M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m0 16v40H40V56ZM40 112h56v88H40Zm176 88H112v-88h104z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-push-pin{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m235.32 81.37l-60.69-60.68a16 16 0 0 0-22.63 0l-53.63 53.8c-10.66-3.34-35-7.37-60.4 13.14a16 16 0 0 0-1.29 23.78L85 159.71l-42.66 42.63a8 8 0 0 0 11.32 11.32L96.29 171l48.29 48.29A16 16 0 0 0 155.9 224h1.13a15.93 15.93 0 0 0 11.64-6.33c19.64-26.1 17.75-47.32 13.19-60L235.33 104a16 16 0 0 0-.01-22.63M224 92.69l-57.27 57.46a8 8 0 0 0-1.49 9.22c9.46 18.93-1.8 38.59-9.34 48.62L48 100.08c12.08-9.74 23.64-12.31 32.48-12.31A40.1 40.1 0 0 1 96.81 91a8 8 0 0 0 9.25-1.51L163.32 32L224 92.68Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-push-pin-fill{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m235.33 104l-53.47 53.65c4.56 12.67 6.45 33.89-13.19 60A15.93 15.93 0 0 1 157 224h-1.13a16 16 0 0 1-11.32-4.69L96.29 171l-42.63 42.66a8 8 0 0 1-11.32-11.32L85 159.71l-48.3-48.3A16 16 0 0 1 38 87.63c25.42-20.51 49.75-16.48 60.4-13.14L152 20.7a16 16 0 0 1 22.63 0l60.69 60.68a16 16 0 0 1 .01 22.62'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-rocket-launch-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M184 120v61.65a8 8 0 0 1-2.34 5.65l-34.35 34.35a8 8 0 0 1-13.57-4.53L128 176Zm-48-48H74.35a8 8 0 0 0-5.65 2.34l-34.35 34.35a8 8 0 0 0 4.53 13.57L80 128ZM40 216c37.65 0 50.69-19.69 54.56-28.18l-26.38-26.38C59.69 165.31 40 178.35 40 216' opacity='.2'/%3E%3Cpath d='M223.85 47.12a16 16 0 0 0-15-15c-12.58-.75-44.73.4-71.41 27.07L132.69 64H74.36A15.9 15.9 0 0 0 63 68.68L28.7 103a16 16 0 0 0 9.07 27.16l38.47 5.37l44.21 44.21l5.37 38.49a15.94 15.94 0 0 0 10.78 12.92a16.1 16.1 0 0 0 5.1.83a15.9 15.9 0 0 0 11.3-4.68l34.32-34.3a15.9 15.9 0 0 0 4.68-11.36v-58.33l4.77-4.77c26.68-26.68 27.83-58.83 27.08-71.42M74.36 80h42.33l-39.53 39.52L40 114.34Zm74.41-9.45a76.65 76.65 0 0 1 59.11-22.47a76.46 76.46 0 0 1-22.42 59.16L128 164.68L91.32 128ZM176 181.64L141.67 216l-5.19-37.17L176 139.31Zm-74.16 9.5C97.34 201 82.29 224 40 224a8 8 0 0 1-8-8c0-42.29 23-57.34 32.86-61.85a8 8 0 0 1 6.64 14.56c-6.43 2.93-20.62 12.36-23.12 38.91c26.55-2.5 36-16.69 38.91-23.12a8 8 0 1 1 14.56 6.64Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-square-half-bottom-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 128v72a8 8 0 0 1-8 8H56a8 8 0 0 1-8-8v-72Z' opacity='.2'/%3E%3Cpath d='M200 40H56a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m0 16v64H56V56Zm0 144H56v-64h144z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-trash-duotone,.i-ph\\:trash-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M200 56v152a8 8 0 0 1-8 8H64a8 8 0 0 1-8-8V56Z' opacity='.2'/%3E%3Cpath d='M216 48h-40v-8a24 24 0 0 0-24-24h-48a24 24 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16V64h8a8 8 0 0 0 0-16M96 40a8 8 0 0 1 8-8h48a8 8 0 0 1 8 8v8H96Zm96 168H64V64h128Zm-80-104v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0m48 0v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-warning-duotone,.i-ph\\:warning-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M215.46 216H40.54c-12.62 0-20.54-13.21-14.41-23.91l87.46-151.87c6.3-11 22.52-11 28.82 0l87.46 151.87c6.13 10.7-1.79 23.91-14.41 23.91' opacity='.2'/%3E%3Cpath d='M236.8 188.09L149.35 36.22a24.76 24.76 0 0 0-42.7 0L19.2 188.09a23.51 23.51 0 0 0 0 23.72A24.35 24.35 0 0 0 40.55 224h174.9a24.35 24.35 0 0 0 21.33-12.19a23.51 23.51 0 0 0 .02-23.72m-13.87 15.71a8.5 8.5 0 0 1-7.48 4.2H40.55a8.5 8.5 0 0 1-7.48-4.2a7.59 7.59 0 0 1 0-7.72l87.45-151.87a8.75 8.75 0 0 1 15 0l87.45 151.87a7.59 7.59 0 0 1-.04 7.72M120 144v-40a8 8 0 0 1 16 0v40a8 8 0 0 1-16 0m20 36a12 12 0 1 1-12-12a12 12 0 0 1 12 12'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph-x{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M205.66 194.34a8 8 0 0 1-11.32 11.32L128 139.31l-66.34 66.35a8 8 0 0 1-11.32-11.32L116.69 128L50.34 61.66a8 8 0 0 1 11.32-11.32L128 116.69l66.34-66.35a8 8 0 0 1 11.32 11.32L139.31 128Z'/%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:bell-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 192H48a8 8 0 0 1-6.88-12C47.71 168.6 56 139.81 56 104a72 72 0 0 1 144 0c0 35.82 8.3 64.6 14.9 76a8 8 0 0 1-6.9 12' opacity='.2'/%3E%3Cpath d='M221.8 175.94c-5.55-9.56-13.8-36.61-13.8-71.94a80 80 0 1 0-160 0c0 35.34-8.26 62.38-13.81 71.94A16 16 0 0 0 48 200h40.81a40 40 0 0 0 78.38 0H208a16 16 0 0 0 13.8-24.06M128 216a24 24 0 0 1-22.62-16h45.24A24 24 0 0 1 128 216m-80-32c7.7-13.24 16-43.92 16-80a64 64 0 1 1 128 0c0 36.05 8.28 66.73 16 80Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:bell-slash-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 192H48a8 8 0 0 1-6.88-12C47.71 168.6 56 139.81 56 104a72 72 0 0 1 144 0c0 35.82 8.3 64.6 14.9 76a8 8 0 0 1-6.9 12' opacity='.2'/%3E%3Cpath d='M53.92 34.62a8 8 0 1 0-11.84 10.76L58.82 63.8A79.6 79.6 0 0 0 48 104c0 35.34-8.26 62.38-13.81 71.94A16 16 0 0 0 48 200h40.8a40 40 0 0 0 78.4 0h15.44l19.44 21.38a8 8 0 1 0 11.84-10.76ZM128 216a24 24 0 0 1-22.62-16h45.24A24 24 0 0 1 128 216m-80-32c7.7-13.24 16-43.92 16-80a63.65 63.65 0 0 1 6.26-27.62L168.09 184Zm166-4.73a8.1 8.1 0 0 1-2.93.55a8 8 0 0 1-7.44-5.08C196.35 156.19 192 129.75 192 104a64 64 0 0 0-95.57-55.69a8 8 0 0 1-7.9-13.91A80 80 0 0 1 208 104c0 35.35 8.05 58.59 10.52 64.88a8 8 0 0 1-4.52 10.37Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:bug-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 128v16a80 80 0 0 1-160 0v-16Z' opacity='.2'/%3E%3Cpath d='M144 92a12 12 0 1 1 12 12a12 12 0 0 1-12-12m-44-12a12 12 0 1 0 12 12a12 12 0 0 0-12-12m116 64a87.8 87.8 0 0 1-3 23l22.24 9.72A8 8 0 0 1 232 192a7.9 7.9 0 0 1-3.2-.67L207.38 182a88 88 0 0 1-158.76 0l-21.42 9.33a7.9 7.9 0 0 1-3.2.67a8 8 0 0 1-3.2-15.33L43 167a87.8 87.8 0 0 1-3-23v-8H16a8 8 0 0 1 0-16h24v-8a87.8 87.8 0 0 1 3-23l-22.2-9.67a8 8 0 1 1 6.4-14.66L48.62 74a88 88 0 0 1 158.76 0l21.42-9.36a8 8 0 0 1 6.4 14.66L213 89.05a87.8 87.8 0 0 1 3 23v8h24a8 8 0 0 1 0 16h-24ZM56 120h144v-8a72 72 0 0 0-144 0Zm64 95.54V136H56v8a72.08 72.08 0 0 0 64 71.54M200 144v-8h-64v79.54A72.08 72.08 0 0 0 200 144'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:check-circle-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M173.66 98.34a8 8 0 0 1 0 11.32l-56 56a8 8 0 0 1-11.32 0l-24-24a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 0M232 128A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:file-code-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M208 88h-56V32Z' opacity='.2'/%3E%3Cpath d='M181.66 146.34a8 8 0 0 1 0 11.32l-24 24a8 8 0 0 1-11.32-11.32L164.69 152l-18.35-18.34a8 8 0 0 1 11.32-11.32Zm-72-24a8 8 0 0 0-11.32 0l-24 24a8 8 0 0 0 0 11.32l24 24a8 8 0 0 0 11.32-11.32L91.31 152l18.35-18.34a8 8 0 0 0 0-11.32M216 88v128a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V40a16 16 0 0 1 16-16h96a8 8 0 0 1 5.66 2.34l56 56A8 8 0 0 1 216 88m-56-8h28.69L160 51.31Zm40 136V96h-48a8 8 0 0 1-8-8V40H56v176z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:funnel-x-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M221.9 61.38L152 136v58.65a8 8 0 0 1-3.56 6.66l-32 21.33A8 8 0 0 1 104 216v-80L34.1 61.38A8 8 0 0 1 40 48h176a8 8 0 0 1 5.9 13.38' opacity='.2'/%3E%3Cpath d='M227.82 66.76A16 16 0 0 0 216 40H40a16 16 0 0 0-11.81 26.76l.08.09L96 139.17V216a16 16 0 0 0 24.87 13.32l32-21.34a16 16 0 0 0 7.13-13.32v-55.49l67.73-72.32Zm-81.63 63.83A8 8 0 0 0 144 136v58.66L112 216v-80a8 8 0 0 0-2.16-5.46L40 56h176Zm99.49 79.81a8 8 0 0 1-11.32 11.32L216 203.32l-18.34 18.35a8 8 0 0 1-11.31-11.32L204.69 192l-18.34-18.35a8 8 0 0 1 11.31-11.31L216 180.69l18.34-18.34a8 8 0 0 1 11.32 11.31L227.31 192Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:globe-simple-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M128 24a104 104 0 1 0 104 104A104.12 104.12 0 0 0 128 24m87.62 96h-39.83c-1.79-36.51-15.85-62.33-27.38-77.6a88.19 88.19 0 0 1 67.22 77.6ZM96.23 136h63.54c-2.31 41.61-22.23 67.11-31.77 77c-9.55-9.9-29.46-35.4-31.77-77m0-16c2.31-41.61 22.23-67.11 31.77-77c9.55 9.93 29.46 35.43 31.77 77Zm11.36-77.6C96.06 57.67 82 83.49 80.21 120H40.37a88.19 88.19 0 0 1 67.22-77.6M40.37 136h39.84c1.82 36.51 15.85 62.33 27.38 77.6A88.19 88.19 0 0 1 40.37 136m108 77.6c11.53-15.27 25.56-41.09 27.38-77.6h39.84a88.19 88.19 0 0 1-67.18 77.6Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:hexagon-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 80.18v95.64a8 8 0 0 1-4.16 7l-88 48.18a8 8 0 0 1-7.68 0l-88-48.18a8 8 0 0 1-4.16-7V80.18a8 8 0 0 1 4.16-7l88-48.18a8 8 0 0 1 7.68 0l88 48.18a8 8 0 0 1 4.16 7' opacity='.2'/%3E%3Cpath d='m223.68 66.15l-88-48.15a15.88 15.88 0 0 0-15.36 0l-88 48.17a16 16 0 0 0-8.32 14v95.64a16 16 0 0 0 8.32 14l88 48.17a15.88 15.88 0 0 0 15.36 0l88-48.17a16 16 0 0 0 8.32-14V80.18a16 16 0 0 0-8.32-14.03M216 175.82L128 224l-88-48.18V80.18L128 32l88 48.17Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:info-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M144 176a8 8 0 0 1-8 8a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 8 8m88-48A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88m-92-32a12 12 0 1 0-12-12a12 12 0 0 0 12 12'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:sort-ascending-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 80v88l-24 24H48V64h160a16 16 0 0 1 16 16' opacity='.2'/%3E%3Cpath d='M128 128a8 8 0 0 1-8 8H48a8 8 0 0 1 0-16h72a8 8 0 0 1 8 8M48 72h136a8 8 0 0 0 0-16H48a8 8 0 0 0 0 16m56 112H48a8 8 0 0 0 0 16h56a8 8 0 0 0 0-16m125.66-21.66a8 8 0 0 0-11.32 0L192 188.69V112a8 8 0 0 0-16 0v76.69l-26.34-26.35a8 8 0 0 0-11.32 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:sort-descending-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 88v88a16 16 0 0 1-16 16H48V64h152Z' opacity='.2'/%3E%3Cpath d='M40 128a8 8 0 0 1 8-8h72a8 8 0 0 1 0 16H48a8 8 0 0 1-8-8m8-56h56a8 8 0 0 0 0-16H48a8 8 0 0 0 0 16m136 112H48a8 8 0 0 0 0 16h136a8 8 0 0 0 0-16m45.66-101.66l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 11.32 11.32L176 67.31V144a8 8 0 0 0 16 0V67.31l26.34 26.35a8 8 0 0 0 11.32-11.32'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:timer-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M216 136a88 88 0 1 1-88-88a88 88 0 0 1 88 88' opacity='.2'/%3E%3Cpath d='M128 40a96 96 0 1 0 96 96a96.11 96.11 0 0 0-96-96m0 176a80 80 0 1 1 80-80a80.09 80.09 0 0 1-80 80m45.66-125.66a8 8 0 0 1 0 11.32l-40 40a8 8 0 0 1-11.32-11.32l40-40a8 8 0 0 1 11.32 0M96 16a8 8 0 0 1 8-8h48a8 8 0 0 1 0 16h-48a8 8 0 0 1-8-8'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:warning-diamond-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='m229.67 133.62l-96 96a7.94 7.94 0 0 1-11.24 0l-96-96a7.94 7.94 0 0 1 0-11.24l96.05-96a7.94 7.94 0 0 1 11.24 0l96 96.05a7.94 7.94 0 0 1-.05 11.19' opacity='.2'/%3E%3Cpath d='M128 72a8 8 0 0 1 8 8v56a8 8 0 0 1-16 0V80a8 8 0 0 1 8-8m-12 100a12 12 0 1 0 12-12a12 12 0 0 0-12 12m124-44a15.85 15.85 0 0 1-4.67 11.28l-96.05 96.06a16 16 0 0 1-22.56 0l-96-96.06a16 16 0 0 1 0-22.56l96.05-96.06a16 16 0 0 1 22.56 0l96.05 96.06A15.85 15.85 0 0 1 240 128m-16 0l-96-96l-96 96l96 96Z'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-ph\\:x-circle-duotone{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M224 128a96 96 0 1 1-96-96a96 96 0 0 1 96 96' opacity='.2'/%3E%3Cpath d='M165.66 101.66L139.31 128l26.35 26.34a8 8 0 0 1-11.32 11.32L128 139.31l-26.34 26.35a8 8 0 0 1-11.32-11.32L116.69 128l-26.35-26.34a8 8 0 0 1 11.32-11.32L128 116.69l26.34-26.35a8 8 0 0 1 11.32 11.32M232 128A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.i-svg-spinners-8-dots-rotate{--vdt-icon:url(\"data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg%3E%3Ccircle cx='3' cy='12' r='2' fill='currentColor'/%3E%3Ccircle cx='21' cy='12' r='2' fill='currentColor'/%3E%3Ccircle cx='12' cy='21' r='2' fill='currentColor'/%3E%3Ccircle cx='12' cy='3' r='2' fill='currentColor'/%3E%3Ccircle cx='5.64' cy='5.64' r='2' fill='currentColor'/%3E%3Ccircle cx='18.36' cy='18.36' r='2' fill='currentColor'/%3E%3Ccircle cx='5.64' cy='18.36' r='2' fill='currentColor'/%3E%3Ccircle cx='18.36' cy='5.64' r='2' fill='currentColor'/%3E%3CanimateTransform attributeName='transform' dur='1.5s' repeatCount='indefinite' type='rotate' values='0 12 12;360 12 12'/%3E%3C/g%3E%3C/svg%3E\");-webkit-mask:var(--vdt-icon) no-repeat;mask:var(--vdt-icon) no-repeat;color:inherit;background-color:currentColor;width:1em;height:1em;mask-size:100% 100%}.container{width:100%}.z-floating-anchor{z-index:2147483644}.z-floating-tooltip{z-index:2147483645}.border-base{--vdt-border-opacity:.13;border-color:rgba(136,136,136,var(--vdt-border-opacity))}.hover\\:border-base:hover{--vdt-border-opacity:.13;border-color:rgba(136,136,136,var(--vdt-border-opacity))}.bg-active{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.bg-base{--vdt-bg-opacity:1;background-color:rgba(255,255,255,var(--vdt-bg-opacity))}.bg-glass{--vdt-backdrop-blur:blur(7px);backdrop-filter:var(--vdt-backdrop-blur) var(--vdt-backdrop-brightness) var(--vdt-backdrop-contrast) var(--vdt-backdrop-grayscale) var(--vdt-backdrop-hue-rotate) var(--vdt-backdrop-invert) var(--vdt-backdrop-opacity) var(--vdt-backdrop-saturate) var(--vdt-backdrop-sepia);background-color:rgba(255,255,255,.5)}.bg-glass\\:75{--vdt-backdrop-blur:blur(7px);backdrop-filter:var(--vdt-backdrop-blur) var(--vdt-backdrop-brightness) var(--vdt-backdrop-contrast) var(--vdt-backdrop-grayscale) var(--vdt-backdrop-hue-rotate) var(--vdt-backdrop-invert) var(--vdt-backdrop-opacity) var(--vdt-backdrop-saturate) var(--vdt-backdrop-sepia);background-color:rgba(255,255,255,.75)}.hover\\:bg-active:hover{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.color-base{--vdt-text-opacity:1;color:rgba(38,38,38,var(--vdt-text-opacity))}@media (prefers-color-scheme:dark){.bg-base{--vdt-bg-opacity:1;background-color:rgba(17,17,17,var(--vdt-bg-opacity))}.bg-glass{background-color:rgba(17,17,17,.5)}.bg-glass\\:75{background-color:rgba(17,17,17,.75)}.color-base{--vdt-text-opacity:1;color:rgba(229,229,229,var(--vdt-text-opacity))}}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.disabled\\:pointer-events-none:disabled{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{top:0;bottom:0;left:0;right:0}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.bottom-4px{bottom:4px}.left--1{left:-.25rem}.left-0{left:0}.left-1\\/2{left:50%}.right--1{right:-.25rem}.right--1px{right:-1px}.right-0{right:0}.right-4{right:1rem}.top-0\\.5{top:.125rem}.top-1{top:.25rem}.top-1\\/2{top:50%}.top-4px{top:4px}.z--1{z-index:-1}.z-2147483647{z-index:2147483647}.grid{display:grid}.cols-\\[max-content_1fr\\]{grid-template-columns:max-content 1fr}.grid-cols-\\[1fr_1fr\\]{grid-template-columns:1fr 1fr}.grid-rows-\\[max-content_1fr\\]{grid-template-rows:max-content 1fr}.m-auto,.ma{margin:auto}.m1{margin:.25rem}.mx--1{margin-left:-.25rem;margin-right:-.25rem}.mx-0\\.5{margin-left:.125rem;margin-right:.125rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.mt-0\\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt2{margin-top:.5rem}.mt6{margin-top:1.5rem}.mt8{margin-top:2rem}.box-border{box-sizing:border-box}.inline{display:inline}.hidden{display:none}.h-1\\.5{height:.375rem}.h-10{height:2.5rem}.h-20{height:5rem}.h-20px{height:20px}.h-3{height:.75rem}.h-3\\.5{height:.875rem}.h-4{height:1rem}.h-4\\.5{height:1.125rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-w-150{max-width:37.5rem}.max-w-200{max-width:50rem}.max-w-220px{max-width:220px}.min-h-0{min-height:0}.min-w-0{min-width:0}.min-w-28{min-width:7rem}.min-w-36{min-width:9rem}.w-\\[40px\\]{width:40px}.w-1\\.5{width:.375rem}.w-10{width:2.5rem}.w-2\\.5{width:.625rem}.w-20{width:5rem}.w-20px{width:20px}.w-2px{width:2px}.w-3{width:.75rem}.w-3\\.5{width:.875rem}.w-4{width:1rem}.w-4\\.5{width:1.125rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.w-px{width:1px}.w-screen{width:100vw}.flex{display:flex}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.shrink-0{flex-shrink:0}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.translate-x--1\\/2{--vdt-translate-x:-50%;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-x-1{--vdt-translate-x:.25rem;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-x-4{--vdt-translate-x:1rem;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-x-5{--vdt-translate-x:1.25rem;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.translate-y--1\\/2{--vdt-translate-y:-50%;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate--45{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:-45deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-0{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:0deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-180{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:180deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-270{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:270deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.rotate-90{--vdt-rotate-x:0;--vdt-rotate-y:0;--vdt-rotate-z:0;--vdt-rotate:90deg;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.scale-120{--vdt-scale-x:1.2;--vdt-scale-y:1.2;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.hover\\:scale-110:hover{--vdt-scale-x:1.1;--vdt-scale-y:1.1;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.scale-y--100{--vdt-scale-y:-1;transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}.transform{transform:translateX(var(--vdt-translate-x)) translateY(var(--vdt-translate-y)) translateZ(var(--vdt-translate-z)) rotate(var(--vdt-rotate)) rotateX(var(--vdt-rotate-x)) rotateY(var(--vdt-rotate-y)) rotateZ(var(--vdt-rotate-z)) skewX(var(--vdt-skew-x)) skewY(var(--vdt-skew-y)) scaleX(var(--vdt-scale-x)) scaleY(var(--vdt-scale-y)) scaleZ(var(--vdt-scale-z))}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.cursor-pointer{cursor:pointer}.select-none{user-select:none}.resize{resize:both}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.gap-0{gap:0}.gap-0\\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.of-auto,.overflow-auto{overflow:auto}.of-hidden,.overflow-hidden{overflow:hidden}.of-x-auto,.overflow-x-auto{overflow-x:auto}.of-y-auto{overflow-y:auto}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.whitespace-pre-wrap{white-space:pre-wrap}.ws-nowrap{white-space:nowrap}.border{border-width:1px}.border-1\\.5{border-width:1.5px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0}.border-b-1\\.5{border-bottom-width:1.5px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-r-1\\.5{border-right-width:1.5px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0}.border-amber\\/20{border-color:rgba(251,191,36,.2)}.border-current{border-color:currentColor}.border-transparent{border-color:transparent}.focus-within\\:border-gray\\/15:focus-within{border-color:rgba(156,163,175,.15)}.hover\\:border-gray\\/10:hover{border-color:rgba(156,163,175,.1)}.focus\\:border-purple:focus{--vdt-border-opacity:1;border-color:rgba(192,132,252,var(--vdt-border-opacity))}.focus\\:border-violet:focus{--vdt-border-opacity:1;border-color:rgba(167,139,250,var(--vdt-border-opacity))}.border-t-transparent{border-top-color:transparent}.rounded{-webkit-border-radius:.25rem;border-radius:.25rem}.rounded-full{-webkit-border-radius:9999px;border-radius:9999px}.rounded-lg{-webkit-border-radius:.5rem;border-radius:.5rem}.rounded-md{-webkit-border-radius:.375rem;border-radius:.375rem}.rounded-xl{-webkit-border-radius:.75rem;border-radius:.75rem}.rounded-r{-webkit-border-top-right-radius:.25rem;border-top-right-radius:.25rem;-webkit-border-bottom-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-t{-webkit-border-top-left-radius:.25rem;border-top-left-radius:.25rem;-webkit-border-top-right-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-md{-webkit-border-top-left-radius:.375rem;border-top-left-radius:.375rem;-webkit-border-top-right-radius:.375rem;border-top-right-radius:.375rem}.bg-amber{--vdt-bg-opacity:1;background-color:rgba(251,191,36,var(--vdt-bg-opacity))}.bg-amber\\/10{background-color:rgba(251,191,36,.1)}.bg-black{--vdt-bg-opacity:1;background-color:rgba(0,0,0,var(--vdt-bg-opacity))}.bg-blue{--vdt-bg-opacity:1;background-color:rgba(96,165,250,var(--vdt-bg-opacity))}.bg-gray{--vdt-bg-opacity:1;background-color:rgba(156,163,175,var(--vdt-bg-opacity))}.bg-gray-6{--vdt-bg-opacity:1;background-color:rgba(75,85,99,var(--vdt-bg-opacity))}.bg-gray\\/10{background-color:rgba(156,163,175,.1)}.bg-gray\\/20{background-color:rgba(156,163,175,.2)}.bg-gray\\/30{background-color:rgba(156,163,175,.3)}.bg-gray\\/5{background-color:rgba(156,163,175,.05)}.bg-green{--vdt-bg-opacity:1;background-color:rgba(74,222,128,var(--vdt-bg-opacity))}.bg-green\\:5{background-color:rgba(74,222,128,.05)}.bg-lime{--vdt-bg-opacity:1;background-color:rgba(163,230,53,var(--vdt-bg-opacity))}.bg-lime\\/20{background-color:rgba(163,230,53,.2)}.bg-lime6{--vdt-bg-opacity:1;background-color:rgba(101,163,13,var(--vdt-bg-opacity))}.bg-red{--vdt-bg-opacity:1;background-color:rgba(248,113,113,var(--vdt-bg-opacity))}.bg-red\\/10{background-color:rgba(248,113,113,.1)}.bg-transparent{background-color:transparent}.bg-violet{--vdt-bg-opacity:1;background-color:rgba(167,139,250,var(--vdt-bg-opacity))}.bg-white{--vdt-bg-opacity:1;background-color:rgba(255,255,255,var(--vdt-bg-opacity))}.hover\\:bg-\\[\\#8881\\]:hover{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.hover\\:bg-gray\\/10:hover{background-color:rgba(156,163,175,.1)}.hover\\:bg-gray\\/15:hover{background-color:rgba(156,163,175,.15)}.hover\\:bg-gray\\/20:hover{background-color:rgba(156,163,175,.2)}.hover\\:bg-gray\\/5:hover{background-color:rgba(156,163,175,.05)}.hover\\:bg-lime7:hover{--vdt-bg-opacity:1;background-color:rgba(77,124,15,var(--vdt-bg-opacity))}.hover\\:bg-red\\/20:hover{background-color:rgba(248,113,113,.2)}.disabled\\:bg-gray6\\!:disabled{--vdt-bg-opacity:1!important;background-color:rgba(75,85,99,var(--vdt-bg-opacity))!important}.fill-black{--vdt-fill-opacity:1;fill:rgba(0,0,0,var(--vdt-fill-opacity))}.fill-hex-08060D{--vdt-fill-opacity:1;fill:rgba(8,6,13,var(--vdt-fill-opacity))}.p-0\\.5{padding:.125rem}.p-1,.p1{padding:.25rem}.p-2,.p2{padding:.5rem}.p-4{padding:1rem}.p1\\.5{padding:.375rem}.p10{padding:2.5rem}.p20{padding:5rem}.p3{padding:.75rem}.px,.px-4,.px4{padding-left:1rem;padding-right:1rem}.px-1,.px1{padding-left:.25rem;padding-right:.25rem}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2,.px2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3,.px3{padding-left:.75rem;padding-right:.75rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py1{padding-top:.25rem;padding-bottom:.25rem}.py1\\.5{padding-top:.375rem;padding-bottom:.375rem}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.text-left{text-align:left}.text-\\[15px\\]{font-size:15px}.text-0\\.6em{font-size:.6em}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.text-amber{--vdt-text-opacity:1;color:rgba(251,191,36,var(--vdt-text-opacity))}.text-amber-800{--vdt-text-opacity:1;color:rgba(146,64,14,var(--vdt-text-opacity))}.text-blue{--vdt-text-opacity:1;color:rgba(96,165,250,var(--vdt-text-opacity))}.text-gray{--vdt-text-opacity:1;color:rgba(156,163,175,var(--vdt-text-opacity))}.text-green{--vdt-text-opacity:1;color:rgba(74,222,128,var(--vdt-text-opacity))}.text-green-800{--vdt-text-opacity:1;color:rgba(22,101,52,var(--vdt-text-opacity))}.text-lime{--vdt-text-opacity:1;color:rgba(163,230,53,var(--vdt-text-opacity))}.text-orange{--vdt-text-opacity:1;color:rgba(251,146,60,var(--vdt-text-opacity))}.text-primary{--vdt-text-opacity:1;color:rgba(213,119,255,var(--vdt-text-opacity))}.text-purple{--vdt-text-opacity:1;color:rgba(192,132,252,var(--vdt-text-opacity))}.text-red{--vdt-text-opacity:1;color:rgba(248,113,113,var(--vdt-text-opacity))}.text-violet{--vdt-text-opacity:1;color:rgba(167,139,250,var(--vdt-text-opacity))}.text-white{--vdt-text-opacity:1;color:rgba(255,255,255,var(--vdt-text-opacity))}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.capitalize{text-transform:capitalize}.line-through{text-decoration-line:line-through}.hover\\:underline:hover{text-decoration-line:underline}.tab{tab-size:4}.op0,.opacity-0{opacity:0}.op100,.opacity-100{opacity:1}.group:hover .group-hover\\:opacity-100{opacity:1}.op30{opacity:.3}.op40{opacity:.4}.op50{opacity:.5}.group:hover .group-hover\\:op50{opacity:.5}.op60{opacity:.6}.op60\\!{opacity:.6!important}.op70{opacity:.7}.op75{opacity:.75}.op80{opacity:.8}.op85{opacity:.85}.hover\\:op100:hover{opacity:1}.hover\\:op100\\!:hover{opacity:1!important}.hover\\:op70:hover{opacity:.7}.hover\\:op80:hover{opacity:.8}.disabled\\:op40:disabled{opacity:.4}.shadow{--vdt-shadow:var(--vdt-shadow-inset) 0 1px 3px 0 var(--vdt-shadow-color,rgba(0,0,0,.1)),var(--vdt-shadow-inset) 0 1px 2px -1px var(--vdt-shadow-color,rgba(0,0,0,.1));box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.shadow-xl{--vdt-shadow:var(--vdt-shadow-inset) 0 20px 25px -5px var(--vdt-shadow-color,rgba(0,0,0,.1)),var(--vdt-shadow-inset) 0 8px 10px -6px var(--vdt-shadow-color,rgba(0,0,0,.1));box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.outline-none{outline-offset:2px;outline:2px solid transparent}.saturate-0{--vdt-saturate:saturate(0);filter:var(--vdt-blur) var(--vdt-brightness) var(--vdt-contrast) var(--vdt-drop-shadow) var(--vdt-grayscale) var(--vdt-hue-rotate) var(--vdt-invert) var(--vdt-saturate) var(--vdt-sepia)}.filter{filter:var(--vdt-blur) var(--vdt-brightness) var(--vdt-contrast) var(--vdt-drop-shadow) var(--vdt-grayscale) var(--vdt-hue-rotate) var(--vdt-invert) var(--vdt-saturate) var(--vdt-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.delay-200{transition-delay:.2s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@media (prefers-color-scheme:dark){.dark-hidden{display:none}.dark\\:fill-hex-fff,.dark\\:fill-white{--vdt-fill-opacity:1;fill:rgba(255,255,255,var(--vdt-fill-opacity))}.dark\\:text-amber-200{--vdt-text-opacity:1;color:rgba(253,230,138,var(--vdt-text-opacity))}.dark\\:text-green-200{--vdt-text-opacity:1;color:rgba(187,247,208,var(--vdt-text-opacity))}}@media (prefers-color-scheme:light){.light-hidden{display:none}}" diff --git a/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue b/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue index a9c7430a..95f9d275 100644 --- a/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue +++ b/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue @@ -1,10 +1,21 @@ diff --git a/packages/core/src/node/auth-state.ts b/packages/core/src/node/auth-state.ts new file mode 100644 index 00000000..46d7b766 --- /dev/null +++ b/packages/core/src/node/auth-state.ts @@ -0,0 +1,87 @@ +import type { DevToolsNodeRpcSession } from '@vitejs/devtools-kit' +import type { SharedState } from '@vitejs/devtools-kit/utils/shared-state' +import type { InternalAnonymousAuthStorage } from './context-internal' +import { humanId } from 'human-id' + +export interface PendingAuthRequest { + clientAuthId: string + session: DevToolsNodeRpcSession + ua: string + origin: string + resolve: (result: { isTrusted: boolean }) => void + abortController: AbortController + timeout: ReturnType +} + +let pendingAuth: PendingAuthRequest | null = null +let tempAuthId: string = generateTempId() + +function generateTempId(): string { + return humanId({ separator: '-', capitalize: false }) +} + +export function getTempAuthId(): string { + return tempAuthId +} + +export function refreshTempAuthId(): string { + tempAuthId = generateTempId() + return tempAuthId +} + +export function getPendingAuth(): PendingAuthRequest | null { + return pendingAuth +} + +export function setPendingAuth(request: PendingAuthRequest | null): void { + pendingAuth = request +} + +/** + * Abort and clean up any existing pending auth request. + */ +export function abortPendingAuth(): void { + if (pendingAuth) { + pendingAuth.abortController.abort() + clearTimeout(pendingAuth.timeout) + pendingAuth = null + } +} + +/** + * Consume the temp auth ID: verify it matches, trust the pending client, and clean up. + * Returns the client's authId if successful, null otherwise. + */ +export function consumeTempAuthId( + id: string, + storage: SharedState, +): string | null { + if (id !== tempAuthId || !pendingAuth) { + return null + } + + const { clientAuthId, session, ua, origin, resolve } = pendingAuth + + // Trust the pending client + storage.mutate((state) => { + state.trusted[clientAuthId] = { + authId: clientAuthId, + ua, + origin, + timestamp: Date.now(), + } + }) + session.meta.clientAuthId = clientAuthId + session.meta.isTrusted = true + + // Resolve the pending auth RPC call + resolve({ isTrusted: true }) + + // Abort terminal prompt and clean up + abortPendingAuth() + + // Generate a new temp ID for next use + refreshTempAuthId() + + return clientAuthId +} diff --git a/packages/core/src/node/config.ts b/packages/core/src/node/config.ts index 16b5191e..03853fc7 100644 --- a/packages/core/src/node/config.ts +++ b/packages/core/src/node/config.ts @@ -13,6 +13,13 @@ export interface DevToolsConfig extends Partial { * @default true */ clientAuth?: boolean + /** + * Pre-configured auth passwords that are automatically trusted. + * + * Clients connecting with an auth ID matching one of these passwords + * will be auto-approved without a terminal prompt. + */ + clientAuthPasswords?: string[] } export interface ResolvedDevToolsConfig { @@ -29,6 +36,7 @@ export function normalizeDevToolsConfig( config: { ...(isObject(config) ? config : {}), clientAuth: isObject(config) ? (config.clientAuth ?? true) : true, + clientAuthPasswords: isObject(config) ? (config.clientAuthPasswords ?? []) : [], host: isObject(config) ? (config.host ?? host) : host, }, } diff --git a/packages/core/src/node/rpc/anonymous/auth.ts b/packages/core/src/node/rpc/anonymous/auth.ts index 6c714695..c14c5e70 100644 --- a/packages/core/src/node/rpc/anonymous/auth.ts +++ b/packages/core/src/node/rpc/anonymous/auth.ts @@ -1,6 +1,9 @@ +/* eslint-disable no-console */ import * as p from '@clack/prompts' import { defineRpcFunction } from '@vitejs/devtools-kit' import c from 'ansis' +import { abortPendingAuth, refreshTempAuthId, setPendingAuth } from '../../auth-state' +import { MARK_INFO } from '../../constants' import { getInternalContext } from '../../context-internal' export interface DevToolsAuthInput { @@ -13,6 +16,8 @@ export interface DevToolsAuthReturn { isTrusted: boolean } +const AUTH_TIMEOUT_MS = 60_000 + export const anonymousAuth = defineRpcFunction({ name: 'vite:anonymous:auth', type: 'action', @@ -33,6 +38,35 @@ export const anonymousAuth = defineRpcFunction({ } } + // Auto-approve if authId matches a configured password + const passwords = (context.viteConfig.devtools?.config as any)?.clientAuthPasswords as string[] ?? [] + if (passwords.includes(query.authId)) { + storage.mutate((state) => { + state.trusted[query.authId] = { + authId: query.authId, + ua: query.ua, + origin: query.origin, + timestamp: Date.now(), + } + }) + session.meta.clientAuthId = query.authId + session.meta.isTrusted = true + return { + isTrusted: true, + } + } + + // Abort any existing pending auth request + abortPendingAuth() + + // Generate a fresh temp ID for the auth URL + const tempId = refreshTempAuthId() + + // Derive the server URL for the auth link + const serverUrl = context.viteServer?.resolvedUrls?.local?.[0]?.replace(/\/$/, '') + ?? `http://localhost:${context.viteConfig.server.port}` + const authUrl = `${serverUrl}/.devtools/auth?id=${encodeURIComponent(tempId)}` + const message = [ `A browser is requesting permissions to connect to the Vite DevTools.`, '', @@ -40,6 +74,8 @@ export const anonymousAuth = defineRpcFunction({ `Origin : ${c.cyan(c.bold(query.origin || 'Unknown'))}`, `Identifier: ${c.green(c.bold(query.authId))}`, '', + `Auth URL : ${c.cyan(c.underline(authUrl))}`, + '', 'This will allow the browser to interact with the server, make file changes and run commands.', c.red(c.bold('You should only trust your local development browsers.')), ] @@ -49,33 +85,68 @@ export const anonymousAuth = defineRpcFunction({ c.bold(c.yellow(' Vite DevTools Permission Request ')), ) - const answer = await p.confirm({ - message: c.bold(`Do you trust this client (${c.green(c.bold(query.authId))})?`), - initialValue: false, - }) + // Set up abort controller for timeout and external cancellation + const abortController = new AbortController() - if (answer) { - storage.mutate((state) => { - state.trusted[query.authId] = { - authId: query.authId, - ua: query.ua, - origin: query.origin, - timestamp: Date.now(), - } + return new Promise((resolve) => { + const timeout = setTimeout(() => { + abortController.abort() + setPendingAuth(null) + console.log(c.yellow`${MARK_INFO} Auth request timed out for ${c.bold(query.authId)}`) + resolve({ isTrusted: false }) + }, AUTH_TIMEOUT_MS) + + // Register as pending auth so auth-verify endpoint can resolve it + setPendingAuth({ + clientAuthId: query.authId, + session, + ua: query.ua, + origin: query.origin, + resolve, + abortController, + timeout, }) - session.meta.clientAuthId = query.authId - session.meta.isTrusted = true - p.outro(c.green(c.bold(`You have granted permissions to ${c.bold(query.authId)}`))) - return { - isTrusted: true, - } - } + // Show terminal confirm prompt with abort signal + p.confirm({ + message: c.bold(`Do you trust this client (${c.green(c.bold(query.authId))})?`), + initialValue: false, + signal: abortController.signal, + }).then((answer) => { + // If already resolved by auth-verify, ignore + clearTimeout(timeout) + setPendingAuth(null) - p.outro(c.red(c.bold(`You have denied permissions to ${c.bold(query.authId)}`))) - return { - isTrusted: false, - } + if (p.isCancel(answer)) { + // Aborted by auth-verify or timeout — already handled + return + } + + if (answer) { + storage.mutate((state) => { + state.trusted[query.authId] = { + authId: query.authId, + ua: query.ua, + origin: query.origin, + timestamp: Date.now(), + } + }) + session.meta.clientAuthId = query.authId + session.meta.isTrusted = true + + p.outro(c.green(c.bold(`You have granted permissions to ${c.bold(query.authId)}`))) + resolve({ isTrusted: true }) + } + else { + p.outro(c.red(c.bold(`You have denied permissions to ${c.bold(query.authId)}`))) + resolve({ isTrusted: false }) + } + }).catch(() => { + // Abort signal triggered — already handled by timeout or auth-verify + clearTimeout(timeout) + setPendingAuth(null) + }) + }) }, } }, diff --git a/packages/core/src/node/server.ts b/packages/core/src/node/server.ts index 289e9298..3689dd14 100644 --- a/packages/core/src/node/server.ts +++ b/packages/core/src/node/server.ts @@ -1,12 +1,64 @@ import type { CreateWsServerOptions } from './ws' import { DEVTOOLS_CONNECTION_META_FILENAME } from '@vitejs/devtools-kit/constants' -import { createApp, eventHandler, fromNodeMiddleware, toNodeListener } from 'h3' +import { createApp, eventHandler, fromNodeMiddleware, getQuery, toNodeListener } from 'h3' import sirv from 'sirv' import { dirClientStandalone } from '../dirs' +import { consumeTempAuthId } from './auth-state' +import { getInternalContext } from './context-internal' import { createWsServer } from './ws' +function generateAuthPageHtml(): string { + return ` + + + Vite DevTools Authorization + + + +
Verifying...
+ + +` +} + export async function createDevToolsMiddleware(options: CreateWsServerOptions) { const h3 = createApp() + const contextInternal = getInternalContext(options.context) const { rpc, getConnectionMeta } = await createWsServer(options) @@ -15,6 +67,28 @@ export async function createDevToolsMiddleware(options: CreateWsServerOptions) { return event.node.res.end(JSON.stringify(await getConnectionMeta())) })) + h3.use('/auth-verify', eventHandler((event) => { + const { id } = getQuery(event) as { id?: string } + if (!id) { + event.node.res.statusCode = 400 + return event.node.res.end('Missing id parameter') + } + + const clientAuthId = consumeTempAuthId(id, contextInternal.storage.auth) + if (!clientAuthId) { + event.node.res.statusCode = 403 + return event.node.res.end('Invalid or expired auth ID') + } + + event.node.res.setHeader('Content-Type', 'application/json') + return event.node.res.end(JSON.stringify({ authId: clientAuthId })) + })) + + h3.use('/auth', eventHandler((event) => { + event.node.res.setHeader('Content-Type', 'text/html') + return event.node.res.end(generateAuthPageHtml()) + })) + h3.use(fromNodeMiddleware(sirv(dirClientStandalone, { dev: true, single: true, diff --git a/packages/core/src/node/ws.ts b/packages/core/src/node/ws.ts index 1a4c06d1..9e0b8fb1 100644 --- a/packages/core/src/node/ws.ts +++ b/packages/core/src/node/ws.ts @@ -54,6 +54,10 @@ export async function createWsServer(options: CreateWsServerOptions) { meta.isTrusted = true meta.clientAuthId = authId } + else if (authId && ((context.viteConfig.devtools?.config as any)?.clientAuthPasswords ?? []).includes(authId)) { + meta.isTrusted = true + meta.clientAuthId = authId + } wsClients.add(ws) const color = meta.isTrusted ? c.green : c.yellow diff --git a/packages/core/tsdown.config.ts b/packages/core/tsdown.config.ts index 67f33f74..3280d8d1 100644 --- a/packages/core/tsdown.config.ts +++ b/packages/core/tsdown.config.ts @@ -37,6 +37,7 @@ export default defineConfig({ 'csstype', 'dompurify', 'get-port-please', + 'human-id', 'sisteransi', 'vue', 'zod', diff --git a/packages/kit/package.json b/packages/kit/package.json index e7687c56..4e4d0fd4 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -46,11 +46,13 @@ "immer": "catalog:deps" }, "devDependencies": { + "human-id": "catalog:inlined", "tsdown": "catalog:build", "ua-parser-modern": "catalog:frontend", "vite": "catalog:build" }, "inlinedDependencies": { + "human-id": "4.1.3", "ohash": "2.0.11", "ua-parser-modern": "0.1.1" } diff --git a/packages/kit/src/client/rpc.ts b/packages/kit/src/client/rpc.ts index 302a8dbb..13ab9e8d 100644 --- a/packages/kit/src/client/rpc.ts +++ b/packages/kit/src/client/rpc.ts @@ -3,12 +3,12 @@ import type { BirpcOptions, BirpcReturn } from 'birpc' import type { ConnectionMeta, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, EventEmitter, RpcSharedStateHost } from '../types' import type { DevToolsClientContext, DevToolsClientRpcHost, RpcClientEvents } from './docks' import { RpcFunctionsCollectorBase } from '@vitejs/devtools-rpc' +import { humanId } from 'human-id' import { DEVTOOLS_CONNECTION_META_FILENAME, DEVTOOLS_MOUNT_PATH, } from '../constants' import { createEventEmitter } from '../utils/events' -import { nanoid } from '../utils/nanoid' import { createRpcSharedStateClientHost } from './rpc-shared-state' import { createStaticRpcClientMode } from './rpc-static' import { createWsRpcClientMode } from './rpc-ws' @@ -112,7 +112,7 @@ function getConnectionAuthIdFromWindows(userAuthId?: string): string { } if (!value) - value = nanoid() + value = humanId({ separator: '-', capitalize: false }) localStorage.setItem(CONNECTION_AUTH_ID_KEY, value) ;(globalThis as any)[CONNECTION_AUTH_ID_KEY] = value @@ -247,5 +247,17 @@ export async function getDevToolsRpcClient( context.rpc = rpc void mode.requestTrust() + // Listen for auth updates from other tabs (e.g., auth URL page) + try { + const bc = new BroadcastChannel('vite-devtools-auth') + bc.onmessage = (event) => { + if (event.data?.type === 'auth-update' && event.data.authId) { + localStorage.setItem(CONNECTION_AUTH_ID_KEY, event.data.authId) + window.location.reload() + } + } + } + catch {} + return rpc } diff --git a/packages/kit/tsdown.config.ts b/packages/kit/tsdown.config.ts index d89c330b..8e5cce5a 100644 --- a/packages/kit/tsdown.config.ts +++ b/packages/kit/tsdown.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ platform: 'neutral', deps: { onlyBundle: [ + 'human-id', 'ohash', 'ua-parser-modern', ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2ca415b..513f17f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -324,6 +324,9 @@ catalogs: '@clack/prompts': specifier: ^1.1.0 version: 1.1.0 + human-id: + specifier: ^4.1.3 + version: 4.1.3 stream-json: specifier: ^1.9.1 version: 1.9.1 @@ -529,7 +532,7 @@ importers: version: 2.13.1 tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) tsx: specifier: catalog:build version: 4.21.0 @@ -544,7 +547,7 @@ importers: version: 1.17.4(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1) vite: specifier: ^8.0.0 - version: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.0)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vite-plugin-inspect: specifier: catalog:devtools version: 11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -747,9 +750,12 @@ importers: dompurify: specifier: catalog:frontend version: 3.3.3 + human-id: + specifier: catalog:inlined + version: 4.1.3 tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) typescript: specifier: catalog:devtools version: 5.9.3 @@ -761,7 +767,7 @@ importers: version: 0.19.2(@vue/compiler-sfc@3.5.30)(vue-router@5.0.3(@vue/compiler-sfc@3.5.30)(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) vite: specifier: ^8.0.0 - version: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.0)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue: specifier: catalog:frontend version: 3.5.30(typescript@5.9.3) @@ -784,15 +790,18 @@ importers: specifier: catalog:deps version: 11.1.4 devDependencies: + human-id: + specifier: catalog:inlined + version: 4.1.3 tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) ua-parser-modern: specifier: catalog:frontend version: 0.1.1 vite: specifier: ^8.0.0 - version: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.0)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + version: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) packages/rolldown: dependencies: @@ -940,7 +949,7 @@ importers: version: 1.0.0 tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) unocss: specifier: catalog:build version: 66.6.6(@unocss/webpack@66.6.6(webpack@5.104.1(esbuild@0.27.4)))(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -968,7 +977,7 @@ importers: devDependencies: tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) ws: specifier: catalog:deps version: 8.19.0 @@ -1002,7 +1011,7 @@ importers: version: 1.0.0 tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) unocss: specifier: catalog:build version: 66.6.6(@unocss/webpack@66.6.6(webpack@5.104.1(esbuild@0.27.4)))(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -1084,7 +1093,7 @@ importers: version: 5.2.2(@nuxt/kit@4.4.2(magicast@0.5.2))(vue@3.5.30(typescript@5.9.3)) tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) unocss: specifier: catalog:build version: 66.6.6(@unocss/webpack@66.6.6(webpack@5.104.1(esbuild@0.27.4)))(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -1097,7 +1106,7 @@ importers: devDependencies: tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) tsx: specifier: catalog:build version: 4.21.0 @@ -3679,24 +3688,24 @@ packages: engines: {node: '>=20'} hasBin: true - '@vitejs/devtools-kit@0.1.0': - resolution: {integrity: sha512-2pgT0piuxc5XG6N1RODS3BbRQz+OKQk4KRoedM8g3rCEx6P440rUPl5tTTxucKcvPmKNYaS/Jqe1KX7pgSLpeA==} + '@vitejs/devtools-kit@0.1.3': + resolution: {integrity: sha512-nqHtYJ/qyo3lh1i9KwWcS1DWFCUkKZ5L0UWfWScSoxtyOIbFe/b4qKILBNViX8rvdJNsfVOU35kWefPQKtjpig==} peerDependencies: vite: ^8.0.0 - '@vitejs/devtools-rolldown@0.1.0': - resolution: {integrity: sha512-wqRJGBJDPeSXHatFmpl6DKmS/6fdBOJrM33NNdUcTILuyTW5WyC3a8uyURalfsF/pL8ZSvf6Aethd+A+oZJ5cw==} + '@vitejs/devtools-rolldown@0.1.3': + resolution: {integrity: sha512-Ag614GiPeAU3nQ+4YJWEbftV7CFH1a3dSrAYPCGp0J2NYCka+PXPHikvmRcA7XNP21bYIo2g51zGi7vVdbiqkA==} - '@vitejs/devtools-rpc@0.1.0': - resolution: {integrity: sha512-tN3qI2sP4Nablu+oMpUMkpJvQFpD5AIOrqgA8i+ZYa7I0HPAB7h3Vj85FHYwQWfgQH1SCvndH3RfKkEDQFep2w==} + '@vitejs/devtools-rpc@0.1.3': + resolution: {integrity: sha512-7Y8IVE4AHOPVdUH2fp+lZHhZN3ts6tUOqrRSXfTko/1nLNlAd9ltKAiP0KunP3LnR+C8OKd/s61xhiQzNAEONA==} peerDependencies: ws: '*' peerDependenciesMeta: ws: optional: true - '@vitejs/devtools@0.1.0': - resolution: {integrity: sha512-jU0g+5mLtXgyFME66rzsLCqXjyScQ5nYK8toI6LEO0gu+1QFK3t3pIRk/PR+RM05X/oeED1YFe+YY2uBunTYMQ==} + '@vitejs/devtools@0.1.3': + resolution: {integrity: sha512-Zj2JYLzROptlw6PTGNdoA60eHQKwaEBilHMDROP35+EeKseZDb8GZmlJCiIw03mI1qUh8XCrA0p8/LHz4N3Bhw==} hasBin: true peerDependencies: vite: ^8.0.0 @@ -5453,6 +5462,10 @@ packages: httpxy@0.1.7: resolution: {integrity: sha512-pXNx8gnANKAndgga5ahefxc++tJvNL87CXoRwxn1cJE2ZkWEojF3tNfQIEhZX/vfpt+wzeAzpUI4qkediX1MLQ==} + human-id@4.1.3: + resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} + hasBin: true + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -10982,24 +10995,24 @@ snapshots: - rollup - supports-color - '@vitejs/devtools-kit@0.1.0(typescript@5.9.3)(vite@8.0.0)(ws@8.19.0)': + '@vitejs/devtools-kit@0.1.3(typescript@5.9.3)(vite@8.0.0)(ws@8.19.0)': dependencies: - '@vitejs/devtools-rpc': 0.1.0(typescript@5.9.3)(ws@8.19.0) + '@vitejs/devtools-rpc': 0.1.3(typescript@5.9.3)(ws@8.19.0) birpc: 4.0.0 immer: 11.1.4 - vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.0)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - typescript - ws optional: true - '@vitejs/devtools-rolldown@0.1.0(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3))': + '@vitejs/devtools-rolldown@0.1.3(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3))': dependencies: '@floating-ui/dom': 1.7.6 '@pnpm/read-project-manifest': 1001.2.5(@pnpm/logger@1001.0.1) '@rolldown/debug': 1.0.0-rc.9 - '@vitejs/devtools-kit': 0.1.0(typescript@5.9.3)(vite@8.0.0)(ws@8.19.0) - '@vitejs/devtools-rpc': 0.1.0(typescript@5.9.3)(ws@8.19.0) + '@vitejs/devtools-kit': 0.1.3(typescript@5.9.3)(vite@8.0.0)(ws@8.19.0) + '@vitejs/devtools-rpc': 0.1.3(typescript@5.9.3)(ws@8.19.0) ansis: 4.2.0 birpc: 4.0.0 cac: 7.0.0 @@ -11049,7 +11062,7 @@ snapshots: - vue optional: true - '@vitejs/devtools-rpc@0.1.0(typescript@5.9.3)(ws@8.19.0)': + '@vitejs/devtools-rpc@0.1.3(typescript@5.9.3)(ws@8.19.0)': dependencies: birpc: 4.0.0 ohash: 2.0.11 @@ -11062,11 +11075,11 @@ snapshots: - typescript optional: true - '@vitejs/devtools@0.1.0(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3))': + '@vitejs/devtools@0.1.3(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3))': dependencies: - '@vitejs/devtools-kit': 0.1.0(typescript@5.9.3)(vite@8.0.0)(ws@8.19.0) - '@vitejs/devtools-rolldown': 0.1.0(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3)) - '@vitejs/devtools-rpc': 0.1.0(typescript@5.9.3)(ws@8.19.0) + '@vitejs/devtools-kit': 0.1.3(typescript@5.9.3)(vite@8.0.0)(ws@8.19.0) + '@vitejs/devtools-rolldown': 0.1.3(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3)) + '@vitejs/devtools-rpc': 0.1.3(typescript@5.9.3)(ws@8.19.0) birpc: 4.0.0 cac: 7.0.0 h3: 1.15.6 @@ -11079,7 +11092,7 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 tinyexec: 1.0.2 - vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.0)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -13050,6 +13063,8 @@ snapshots: httpxy@0.1.7: {} + human-id@4.1.3: {} + human-signals@2.1.0: {} human-signals@5.0.0: {} @@ -15499,7 +15514,7 @@ snapshots: ts-dedent@2.2.0: {} - tsdown@0.21.2(@vitejs/devtools@0.1.0)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)): + tsdown@0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)): dependencies: ansis: 4.2.0 cac: 7.0.0 @@ -15518,7 +15533,7 @@ snapshots: unconfig-core: 7.5.0 unrun: 0.2.32(synckit@0.11.12) optionalDependencies: - '@vitejs/devtools': 0.1.0(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3)) + '@vitejs/devtools': 0.1.3(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3)) publint: 0.3.18 typescript: 5.9.3 transitivePeerDependencies: @@ -16039,7 +16054,7 @@ snapshots: vite: 8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.30(typescript@5.9.3) - vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.0)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@0.1.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@oxc-project/runtime': 0.115.0 lightningcss: 1.32.0 @@ -16049,7 +16064,7 @@ snapshots: tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.0.3 - '@vitejs/devtools': 0.1.0(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3)) + '@vitejs/devtools': 0.1.3(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(typescript@5.9.3)(vite@8.0.0)(vue@3.5.30(typescript@5.9.3)) esbuild: 0.27.4 fsevents: 2.3.3 jiti: 2.6.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 193a2e5c..4f27323a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -133,6 +133,7 @@ catalogs: inlined: '@antfu/utils': ^9.3.0 '@clack/prompts': ^1.1.0 + human-id: ^4.1.3 stream-json: ^1.9.1 playground: unplugin-vue-router: ^0.19.2 From 7308af37320cc9950c62a3bf3705488f32a027b5 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 09:47:24 +0900 Subject: [PATCH 02/16] feat(self-inspect): add auth tokens management tab Add an "Auth Tokens" tab to the self-inspect debug panel that lists all trusted clients with their auth ID, user agent, origin, and trust date. Each token can be revoked individually. - Add get-auth-tokens and revoke-auth-token RPC functions - Export getInternalContext from @vitejs/devtools for cross-package access - Add AuthTokensList component and auth page to self-inspect UI Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/core/src/index.ts | 2 + packages/self-inspect/package.json | 1 + packages/self-inspect/src/app/app.vue | 1 + .../src/app/components/AuthTokensList.vue | 85 ++++ packages/self-inspect/src/app/pages/auth.vue | 32 ++ .../src/node/rpc/functions/get-auth-tokens.ts | 17 + .../node/rpc/functions/revoke-auth-token.ts | 18 + packages/self-inspect/src/node/rpc/index.ts | 4 + pnpm-lock.yaml | 444 +++++++++++++++++- test/exports/@vitejs/devtools.yaml | 1 + 10 files changed, 600 insertions(+), 5 deletions(-) create mode 100644 packages/self-inspect/src/app/components/AuthTokensList.vue create mode 100644 packages/self-inspect/src/app/pages/auth.vue create mode 100644 packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts create mode 100644 packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 85b777e6..887a3734 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,5 @@ export { createDevToolsContext } from './node/context' +export { getInternalContext } from './node/context-internal' +export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from './node/context-internal' export { DevTools } from './node/plugins' export { createDevToolsMiddleware } from './node/server' diff --git a/packages/self-inspect/package.json b/packages/self-inspect/package.json index d31bdf3f..f7db6c22 100644 --- a/packages/self-inspect/package.json +++ b/packages/self-inspect/package.json @@ -32,6 +32,7 @@ "prepack": "pnpm build" }, "dependencies": { + "@vitejs/devtools": "workspace:*", "@vitejs/devtools-kit": "workspace:*", "@vitejs/devtools-rpc": "workspace:*", "birpc": "catalog:deps", diff --git a/packages/self-inspect/src/app/app.vue b/packages/self-inspect/src/app/app.vue index 70706e41..70c54ee1 100644 --- a/packages/self-inspect/src/app/app.vue +++ b/packages/self-inspect/src/app/app.vue @@ -16,6 +16,7 @@ const navItems = [ { title: 'Docks', to: '/docks', icon: 'i-ph-layout-duotone' }, { title: 'Client Scripts', to: '/scripts', icon: 'i-ph-code-duotone' }, { title: 'Plugins', to: '/plugins', icon: 'i-ph-puzzle-piece-duotone' }, + { title: 'Auth Tokens', to: '/auth', icon: 'i-ph-key-duotone' }, ] const { refresh, loading } = useRefresh() diff --git a/packages/self-inspect/src/app/components/AuthTokensList.vue b/packages/self-inspect/src/app/components/AuthTokensList.vue new file mode 100644 index 00000000..d78ca0b8 --- /dev/null +++ b/packages/self-inspect/src/app/components/AuthTokensList.vue @@ -0,0 +1,85 @@ + + + diff --git a/packages/self-inspect/src/app/pages/auth.vue b/packages/self-inspect/src/app/pages/auth.vue new file mode 100644 index 00000000..d96f1e56 --- /dev/null +++ b/packages/self-inspect/src/app/pages/auth.vue @@ -0,0 +1,32 @@ + + + diff --git a/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts b/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts new file mode 100644 index 00000000..e5b6bc92 --- /dev/null +++ b/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts @@ -0,0 +1,17 @@ +import { getInternalContext } from '@vitejs/devtools' +import { defineRpcFunction } from '@vitejs/devtools-kit' + +export const getAuthTokens = defineRpcFunction({ + name: 'devtoolskit:self-inspect:get-auth-tokens', + type: 'query', + setup: (context) => { + const internal = getInternalContext(context) + const storage = internal.storage.auth + return { + handler: async () => { + const trusted = storage.value().trusted + return Object.values(trusted) + }, + } + }, +}) diff --git a/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts b/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts new file mode 100644 index 00000000..6e842587 --- /dev/null +++ b/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts @@ -0,0 +1,18 @@ +import { getInternalContext } from '@vitejs/devtools' +import { defineRpcFunction } from '@vitejs/devtools-kit' + +export const revokeAuthToken = defineRpcFunction({ + name: 'devtoolskit:self-inspect:revoke-auth-token', + type: 'action', + setup: (context) => { + const internal = getInternalContext(context) + const storage = internal.storage.auth + return { + handler: async (authId: string) => { + storage.mutate((state) => { + delete state.trusted[authId] + }) + }, + } + }, +}) diff --git a/packages/self-inspect/src/node/rpc/index.ts b/packages/self-inspect/src/node/rpc/index.ts index cfc88bd7..4e3a1fa1 100644 --- a/packages/self-inspect/src/node/rpc/index.ts +++ b/packages/self-inspect/src/node/rpc/index.ts @@ -1,8 +1,10 @@ import type { RpcDefinitionsFilter, RpcDefinitionsToFunctions } from '@vitejs/devtools-kit' +import { getAuthTokens } from './functions/get-auth-tokens' import { getClientScripts } from './functions/get-client-scripts' import { getDevtoolsPlugins } from './functions/get-devtools-plugins' import { getDocks } from './functions/get-docks' import { getRpcFunctions } from './functions/get-rpc-functions' +import { revokeAuthToken } from './functions/revoke-auth-token' import '@vitejs/devtools-kit' export const rpcFunctions = [ @@ -10,6 +12,8 @@ export const rpcFunctions = [ getRpcFunctions, getClientScripts, getDevtoolsPlugins, + getAuthTokens, + revokeAuthToken, ] as const export type ServerFunctions = RpcDefinitionsToFunctions diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 513f17f8..6b79cdf7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -984,6 +984,9 @@ importers: packages/self-inspect: dependencies: + '@vitejs/devtools': + specifier: workspace:* + version: link:../core '@vitejs/devtools-kit': specifier: workspace:* version: link:../kit @@ -999,25 +1002,25 @@ importers: devDependencies: '@unocss/nuxt': specifier: catalog:build - version: 66.6.6(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.4)) + version: 66.6.6(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.4)) '@vueuse/core': specifier: catalog:frontend version: 14.2.1(vue@3.5.30(typescript@5.9.3)) '@vueuse/nuxt': specifier: catalog:build - version: 14.2.1(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) + version: 14.2.1(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) structured-clone-es: specifier: catalog:deps version: 1.0.0 tsdown: specifier: catalog:build - version: 0.21.2(@vitejs/devtools@0.1.3)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) + version: 0.21.2(@vitejs/devtools@packages+core)(publint@0.3.18)(synckit@0.11.12)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3)) unocss: specifier: catalog:build - version: 66.6.6(@unocss/webpack@66.6.6(webpack@5.104.1(esbuild@0.27.4)))(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 66.6.6(@unocss/webpack@66.6.6(webpack@5.104.1(esbuild@0.27.4)))(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) vite-hot-client: specifier: catalog:frontend - version: 2.1.0(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 2.1.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) packages/ui: dependencies: @@ -9004,6 +9007,14 @@ snapshots: '@nuxt/devalue@2.0.2': {} + '@nuxt/devtools-kit@3.2.3(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@nuxt/kit': 4.4.2(magicast@0.5.2) + execa: 8.0.1 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - magicast + '@nuxt/devtools-kit@3.2.3(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) @@ -9023,6 +9034,49 @@ snapshots: pkg-types: 2.3.0 semver: 7.7.4 + '@nuxt/devtools@3.2.3(@vitejs/devtools@packages+core)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': + dependencies: + '@nuxt/devtools-kit': 3.2.3(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@nuxt/devtools-wizard': 3.2.3 + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@vue/devtools-core': 8.0.7(vue@3.5.30(typescript@5.9.3)) + '@vue/devtools-kit': 8.0.7 + birpc: 4.0.0 + consola: 3.4.2 + destr: 2.0.5 + error-stack-parser-es: 1.0.5 + execa: 8.0.1 + fast-npm-meta: 1.4.2 + get-port-please: 3.2.0 + hookable: 6.0.1 + image-meta: 0.2.2 + is-installed-globally: 1.0.0 + launch-editor: 2.13.1 + local-pkg: 1.1.2 + magicast: 0.5.2 + nypm: 0.6.5 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.0 + semver: 7.7.4 + simple-git: 3.32.3 + sirv: 3.0.2 + structured-clone-es: 1.0.0 + tinyglobby: 0.2.15 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-plugin-inspect: 11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + vite-plugin-vue-tracer: 1.2.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) + which: 5.0.0 + ws: 8.19.0 + optionalDependencies: + '@vitejs/devtools': link:packages/core + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + - vue + '@nuxt/devtools@3.2.3(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': dependencies: '@nuxt/devtools-kit': 3.2.3(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -9157,6 +9211,74 @@ snapshots: transitivePeerDependencies: - magicast + '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(rolldown@1.0.0-rc.9)(typescript@5.9.3)': + dependencies: + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@nuxt/devalue': 2.0.2 + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@unhead/vue': 2.1.12(vue@3.5.30(typescript@5.9.3)) + '@vue/shared': 3.5.30 + consola: 3.4.2 + defu: 6.1.4 + destr: 2.0.5 + devalue: 5.6.4 + errx: 0.1.0 + escape-string-regexp: 5.0.0 + exsolve: 1.0.8 + h3: 1.15.6 + impound: 1.1.5 + klona: 2.0.6 + mocked-exports: 0.1.1 + nitropack: 2.13.1(idb-keyval@6.2.2)(rolldown@1.0.0-rc.9) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2) + nypm: 0.6.5 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.0 + rou3: 0.8.1 + std-env: 4.0.0 + ufo: 1.6.3 + unctx: 2.5.0 + unstorage: 1.17.4(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1) + vue: 3.5.30(typescript@5.9.3) + vue-bundle-renderer: 2.2.0 + vue-devtools-stub: 0.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@babel/core' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bare-abort-controller + - better-sqlite3 + - db0 + - drizzle-orm + - encoding + - idb-keyval + - ioredis + - magicast + - mysql2 + - react-native-b4a + - rolldown + - sqlite3 + - supports-color + - typescript + - uploadthing + - xml2js + '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(rolldown@1.0.0-rc.9)(typescript@5.9.3)': dependencies: '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) @@ -9310,6 +9432,68 @@ snapshots: rc9: 3.0.0 std-env: 3.10.0 + '@nuxt/vite-builder@4.4.2(9b75897fa45d45a02a447dbe67f1220b)': + dependencies: + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@rollup/plugin-replace': 6.0.3(rollup@4.55.1) + '@vitejs/plugin-vue': 6.0.5(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': 5.1.4(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) + autoprefixer: 10.4.27(postcss@8.5.8) + consola: 3.4.2 + cssnano: 7.1.3(postcss@8.5.8) + defu: 6.1.4 + escape-string-regexp: 5.0.0 + exsolve: 1.0.8 + get-port-please: 3.2.0 + jiti: 2.6.1 + knitwork: 1.3.0 + magic-string: 0.30.21 + mlly: 1.8.1 + mocked-exports: 0.1.1 + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2) + nypm: 0.6.5 + pathe: 2.0.3 + pkg-types: 2.3.0 + postcss: 8.5.8 + seroval: 1.5.1 + std-env: 4.0.0 + ufo: 1.6.3 + unenv: 2.0.0-rc.24 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 5.3.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-plugin-checker: 0.12.0(eslint@10.0.3(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)) + vue: 3.5.30(typescript@5.9.3) + vue-bundle-renderer: 2.2.0 + optionalDependencies: + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.0) + rolldown: 1.0.0-rc.9 + rollup-plugin-visualizer: 6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1) + transitivePeerDependencies: + - '@biomejs/biome' + - '@types/node' + - '@vitejs/devtools' + - esbuild + - eslint + - less + - magicast + - meow + - optionator + - oxlint + - rollup + - sass + - sass-embedded + - stylelint + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - vls + - vti + - vue-tsc + - yaml + '@nuxt/vite-builder@4.4.2(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@types/node@25.0.3)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.30(typescript@5.9.3))(yaml@2.8.2)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) @@ -10770,6 +10954,29 @@ snapshots: gzip-size: 6.0.0 sirv: 3.0.2 + '@unocss/nuxt@66.6.6(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.4))': + dependencies: + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@unocss/config': 66.6.6 + '@unocss/core': 66.6.6 + '@unocss/preset-attributify': 66.6.6 + '@unocss/preset-icons': 66.6.6 + '@unocss/preset-tagify': 66.6.6 + '@unocss/preset-typography': 66.6.6 + '@unocss/preset-web-fonts': 66.6.6 + '@unocss/preset-wind3': 66.6.6 + '@unocss/preset-wind4': 66.6.6 + '@unocss/reset': 66.6.6 + '@unocss/vite': 66.6.6(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@unocss/webpack': 66.6.6(webpack@5.104.1(esbuild@0.27.4)) + unocss: 66.6.6(@unocss/webpack@66.6.6(webpack@5.104.1(esbuild@0.27.4)))(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + transitivePeerDependencies: + - '@unocss/astro' + - '@unocss/postcss' + - magicast + - vite + - webpack + '@unocss/nuxt@66.6.6(magicast@0.5.2)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.4))': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) @@ -11121,6 +11328,18 @@ snapshots: - vue optional: true + '@vitejs/plugin-vue-jsx@5.1.4(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-rc.9 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0) + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vue: 3.5.30(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + '@vitejs/plugin-vue-jsx@5.1.4(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': dependencies: '@babel/core': 7.29.0 @@ -11421,6 +11640,17 @@ snapshots: '@vueuse/metadata@14.2.1': {} + '@vueuse/nuxt@14.2.1(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': + dependencies: + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@vueuse/core': 14.2.1(vue@3.5.30(typescript@5.9.3)) + '@vueuse/metadata': 14.2.1 + local-pkg: 1.1.2 + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2) + vue: 3.5.30(typescript@5.9.3) + transitivePeerDependencies: + - magicast + '@vueuse/nuxt@14.2.1(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3))': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) @@ -14110,6 +14340,135 @@ snapshots: dependencies: boolbase: 1.0.0 + nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2): + dependencies: + '@dxup/nuxt': 0.4.0(magicast@0.5.2)(typescript@5.9.3) + '@nuxt/cli': 3.34.0(@nuxt/schema@4.4.2)(cac@6.7.14)(magicast@0.5.2) + '@nuxt/devtools': 3.2.3(@vitejs/devtools@packages+core)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vitejs/devtools@packages+core)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2))(rolldown@1.0.0-rc.9)(typescript@5.9.3) + '@nuxt/schema': 4.4.2 + '@nuxt/telemetry': 2.7.0(@nuxt/kit@4.4.2(magicast@0.5.2)) + '@nuxt/vite-builder': 4.4.2(9b75897fa45d45a02a447dbe67f1220b) + '@unhead/vue': 2.1.12(vue@3.5.30(typescript@5.9.3)) + '@vue/shared': 3.5.30 + c12: 3.3.3(magicast@0.5.2) + chokidar: 5.0.0 + compatx: 0.2.0 + consola: 3.4.2 + cookie-es: 2.0.0 + defu: 6.1.4 + devalue: 5.6.4 + errx: 0.1.0 + escape-string-regexp: 5.0.0 + exsolve: 1.0.8 + hookable: 6.0.1 + ignore: 7.0.5 + impound: 1.1.5 + jiti: 2.6.1 + klona: 2.0.6 + knitwork: 1.3.0 + magic-string: 0.30.21 + mlly: 1.8.1 + nanotar: 0.3.0 + nypm: 0.6.5 + ofetch: 1.5.1 + ohash: 2.0.11 + on-change: 6.0.2 + oxc-minify: 0.117.0 + oxc-parser: 0.117.0 + oxc-transform: 0.117.0 + oxc-walker: 0.7.0(oxc-parser@0.117.0) + pathe: 2.0.3 + perfect-debounce: 2.1.0 + picomatch: 4.0.3 + pkg-types: 2.3.0 + rou3: 0.8.1 + scule: 1.3.0 + semver: 7.7.4 + std-env: 4.0.0 + tinyglobby: 0.2.15 + ufo: 1.6.3 + ultrahtml: 1.6.0 + uncrypto: 0.1.3 + unctx: 2.5.0 + unimport: 6.0.1 + unplugin: 3.0.0 + unrouting: 0.1.5 + untyped: 2.0.0 + vue: 3.5.30(typescript@5.9.3) + vue-router: 5.0.3(@vue/compiler-sfc@3.5.30)(vue@3.5.30(typescript@5.9.3)) + optionalDependencies: + '@parcel/watcher': 2.5.1 + '@types/node': 25.0.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@babel/core' + - '@babel/plugin-proposal-decorators' + - '@babel/plugin-syntax-jsx' + - '@biomejs/biome' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@pinia/colada' + - '@planetscale/database' + - '@rollup/plugin-babel' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@vitejs/devtools' + - '@vue/compiler-sfc' + - aws4fetch + - bare-abort-controller + - better-sqlite3 + - bufferutil + - cac + - commander + - db0 + - drizzle-orm + - encoding + - esbuild + - eslint + - idb-keyval + - ioredis + - less + - magicast + - meow + - mysql2 + - optionator + - oxlint + - pinia + - react-native-b4a + - rolldown + - rollup + - rollup-plugin-visualizer + - sass + - sass-embedded + - sqlite3 + - stylelint + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - utf-8-validate + - vite + - vls + - vti + - vue-tsc + - xml2js + - yaml + nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0))(@parcel/watcher@2.5.1)(@types/node@25.0.3)(@vue/compiler-sfc@3.5.30)(cac@6.7.14)(db0@0.3.4)(esbuild@0.27.4)(eslint@10.0.3(jiti@2.6.1))(idb-keyval@6.2.2)(ioredis@5.9.1)(magicast@0.5.2)(optionator@0.9.4)(rolldown@1.0.0-rc.9)(rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.9)(rollup@4.55.1))(rollup@4.55.1)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3))(yaml@2.8.2): dependencies: '@dxup/nuxt': 0.4.0(magicast@0.5.2)(typescript@5.9.3) @@ -15966,16 +16325,47 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + vite-dev-rpc@1.1.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + birpc: 2.9.0 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-hot-client: 2.1.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + vite-dev-rpc@1.1.0(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: birpc: 2.9.0 vite: 8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vite-hot-client: 2.1.0(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + vite-hot-client@2.1.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-hot-client@2.1.0(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: vite: 8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-node@5.3.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + cac: 6.7.14 + es-module-lexer: 2.0.0 + obug: 2.1.1 + pathe: 2.0.3 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - '@vitejs/devtools' + - esbuild + - jiti + - less + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vite-node@5.3.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 @@ -15997,6 +16387,23 @@ snapshots: - tsx - yaml + vite-plugin-checker@0.12.0(eslint@10.0.3(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)): + dependencies: + '@babel/code-frame': 7.29.0 + chokidar: 5.0.0 + npm-run-path: 6.0.0 + picocolors: 1.1.1 + picomatch: 4.0.3 + tiny-invariant: 1.3.3 + tinyglobby: 0.2.15 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vscode-uri: 3.1.0 + optionalDependencies: + eslint: 10.0.3(jiti@2.6.1) + optionator: 0.9.4 + typescript: 5.9.3 + vue-tsc: 3.2.5(typescript@5.9.3) + vite-plugin-checker@0.12.0(eslint@10.0.3(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)): dependencies: '@babel/code-frame': 7.29.0 @@ -16014,6 +16421,23 @@ snapshots: typescript: 5.9.3 vue-tsc: 3.2.5(typescript@5.9.3) + vite-plugin-inspect@11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + ansis: 4.2.0 + debug: 4.4.3 + error-stack-parser-es: 1.0.5 + ohash: 2.0.11 + open: 10.2.0 + perfect-debounce: 2.1.0 + sirv: 3.0.2 + unplugin-utils: 0.3.1 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vite-dev-rpc: 1.1.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + optionalDependencies: + '@nuxt/kit': 4.4.2(magicast@0.5.2) + transitivePeerDependencies: + - supports-color + vite-plugin-inspect@11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: ansis: 4.2.0 @@ -16044,6 +16468,16 @@ snapshots: transitivePeerDependencies: - supports-color + vite-plugin-vue-tracer@1.2.0(vite@8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)): + dependencies: + estree-walker: 3.0.3 + exsolve: 1.0.8 + magic-string: 0.30.21 + pathe: 2.0.3 + source-map-js: 1.2.1 + vite: 8.0.0(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) + vue: 3.5.30(typescript@5.9.3) + vite-plugin-vue-tracer@1.2.0(vite@8.0.0(@types/node@25.0.3)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 diff --git a/test/exports/@vitejs/devtools.yaml b/test/exports/@vitejs/devtools.yaml index dfd85646..6a2e0c55 100644 --- a/test/exports/@vitejs/devtools.yaml +++ b/test/exports/@vitejs/devtools.yaml @@ -2,6 +2,7 @@ createDevToolsContext: function createDevToolsMiddleware: function DevTools: function + getInternalContext: function ./cli-commands: build: function start: function From 76b45301d4c7afee3b34991e7b29d25fd4e8a09c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 10:16:34 +0900 Subject: [PATCH 03/16] feat(self-inspect): add auth tokens management tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Unify wording: clientAuthPasswords → clientAuthTokens, "password" → "token" in UI - Add revokeAuthToken() utility that removes token from storage and notifies all connected clients using that token via auth:revoked broadcast event - One token can be used by multiple clients; revoking disconnects all of them - Client-side handler in rpc-ws.ts listens for auth:revoked and shows auth notice - Self-inspect revoke RPC now uses the shared revokeAuthToken utility Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ViewBuiltinClientAuthNotice.vue | 14 +++---- packages/core/src/index.ts | 1 + packages/core/src/node/auth-revoke.ts | 41 +++++++++++++++++++ packages/core/src/node/config.ts | 8 ++-- packages/core/src/node/rpc/anonymous/auth.ts | 6 +-- packages/core/src/node/rpc/index.ts | 2 +- packages/core/src/node/ws.ts | 2 +- packages/kit/src/client/rpc-ws.ts | 10 +++++ .../src/app/components/AuthTokensList.vue | 2 +- .../node/rpc/functions/revoke-auth-token.ts | 10 ++--- packages/self-inspect/src/node/rpc/index.ts | 4 +- test/exports/@vitejs/devtools.yaml | 1 + 12 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 packages/core/src/node/auth-revoke.ts diff --git a/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue b/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue index 95f9d275..6ba6e5d5 100644 --- a/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue +++ b/packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue @@ -7,10 +7,10 @@ defineProps<{ context: DocksContext }>() -const passwordInput = ref('') +const tokenInput = ref('') -function submitPassword() { - const value = passwordInput.value.trim() +function submitToken() { + const value = tokenInput.value.trim() if (!value) return localStorage.setItem('__VITE_DEVTOOLS_CONNECTION_AUTH_ID__', value) @@ -37,17 +37,17 @@ function submitPassword() {
or
-
+ diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 887a3734..a439f2e1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,4 @@ +export { revokeAuthToken } from './node/auth-revoke' export { createDevToolsContext } from './node/context' export { getInternalContext } from './node/context-internal' export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from './node/context-internal' diff --git a/packages/core/src/node/auth-revoke.ts b/packages/core/src/node/auth-revoke.ts new file mode 100644 index 00000000..dd803c41 --- /dev/null +++ b/packages/core/src/node/auth-revoke.ts @@ -0,0 +1,41 @@ +import type { DevToolsNodeContext } from '@vitejs/devtools-kit' +import type { RpcFunctionsHost } from './host-functions' +import { getInternalContext } from './context-internal' + +/** + * Revoke an auth token: remove from storage and notify all connected clients + * using this token that they are no longer trusted. + */ +export async function revokeAuthToken(context: DevToolsNodeContext, token: string): Promise { + const internal = getInternalContext(context) + const storage = internal.storage.auth + + // Remove from persistent storage + storage.mutate((state) => { + delete state.trusted[token] + }) + + const rpcHost = context.rpc as unknown as RpcFunctionsHost + if (!rpcHost._rpcGroup) + return + + // Collect affected session IDs before modifying meta + const affectedSessionIds = new Set() + for (const client of rpcHost._rpcGroup.clients) { + if (client.$meta.clientAuthId === token) { + affectedSessionIds.add(client.$meta.id) + client.$meta.isTrusted = false + client.$meta.clientAuthId = undefined! + } + } + + if (affectedSessionIds.size === 0) + return + + // Notify affected clients + await rpcHost.broadcast({ + method: 'devtoolskit:internal:auth:revoked', + args: [], + filter: client => affectedSessionIds.has(client.$meta.id), + }) +} diff --git a/packages/core/src/node/config.ts b/packages/core/src/node/config.ts index 03853fc7..ea99b61a 100644 --- a/packages/core/src/node/config.ts +++ b/packages/core/src/node/config.ts @@ -14,12 +14,12 @@ export interface DevToolsConfig extends Partial { */ clientAuth?: boolean /** - * Pre-configured auth passwords that are automatically trusted. + * Pre-configured auth tokens that are automatically trusted. * - * Clients connecting with an auth ID matching one of these passwords + * Clients connecting with an auth token matching one of these * will be auto-approved without a terminal prompt. */ - clientAuthPasswords?: string[] + clientAuthTokens?: string[] } export interface ResolvedDevToolsConfig { @@ -36,7 +36,7 @@ export function normalizeDevToolsConfig( config: { ...(isObject(config) ? config : {}), clientAuth: isObject(config) ? (config.clientAuth ?? true) : true, - clientAuthPasswords: isObject(config) ? (config.clientAuthPasswords ?? []) : [], + clientAuthTokens: isObject(config) ? (config.clientAuthTokens ?? []) : [], host: isObject(config) ? (config.host ?? host) : host, }, } diff --git a/packages/core/src/node/rpc/anonymous/auth.ts b/packages/core/src/node/rpc/anonymous/auth.ts index c14c5e70..de55ab05 100644 --- a/packages/core/src/node/rpc/anonymous/auth.ts +++ b/packages/core/src/node/rpc/anonymous/auth.ts @@ -38,9 +38,9 @@ export const anonymousAuth = defineRpcFunction({ } } - // Auto-approve if authId matches a configured password - const passwords = (context.viteConfig.devtools?.config as any)?.clientAuthPasswords as string[] ?? [] - if (passwords.includes(query.authId)) { + // Auto-approve if authId matches a configured auth token + const tokens = (context.viteConfig.devtools?.config as any)?.clientAuthTokens as string[] ?? [] + if (tokens.includes(query.authId)) { storage.mutate((state) => { state.trusted[query.authId] = { authId: query.authId, diff --git a/packages/core/src/node/rpc/index.ts b/packages/core/src/node/rpc/index.ts index 6ec8d56d..59ad2818 100644 --- a/packages/core/src/node/rpc/index.ts +++ b/packages/core/src/node/rpc/index.ts @@ -65,7 +65,7 @@ declare module '@vitejs/devtools-kit' { // @keep-sorted export interface DevToolsRpcClientFunctions { - + 'devtoolskit:internal:auth:revoked': () => Promise 'devtoolskit:internal:logs:updated': () => Promise 'devtoolskit:internal:rpc:client-state:patch': (key: string, patches: SharedStatePatch[], syncId: string) => Promise 'devtoolskit:internal:rpc:client-state:updated': (key: string, fullState: any, syncId: string) => Promise diff --git a/packages/core/src/node/ws.ts b/packages/core/src/node/ws.ts index 9e0b8fb1..2e412e5a 100644 --- a/packages/core/src/node/ws.ts +++ b/packages/core/src/node/ws.ts @@ -54,7 +54,7 @@ export async function createWsServer(options: CreateWsServerOptions) { meta.isTrusted = true meta.clientAuthId = authId } - else if (authId && ((context.viteConfig.devtools?.config as any)?.clientAuthPasswords ?? []).includes(authId)) { + else if (authId && ((context.viteConfig.devtools?.config as any)?.clientAuthTokens ?? []).includes(authId)) { meta.isTrusted = true meta.clientAuthId = authId } diff --git a/packages/kit/src/client/rpc-ws.ts b/packages/kit/src/client/rpc-ws.ts index ed02c008..aa6e629c 100644 --- a/packages/kit/src/client/rpc-ws.ts +++ b/packages/kit/src/client/rpc-ws.ts @@ -51,6 +51,16 @@ export function createWsRpcClientMode( }, ) + // Handle server-initiated auth revocation + clientRpc.register({ + name: 'devtoolskit:internal:auth:revoked', + type: 'event', + handler: () => { + isTrusted = false + events.emit('rpc:is-trusted:updated', false) + }, + }) + async function requestTrust() { if (isTrusted) return true diff --git a/packages/self-inspect/src/app/components/AuthTokensList.vue b/packages/self-inspect/src/app/components/AuthTokensList.vue index d78ca0b8..c03a486d 100644 --- a/packages/self-inspect/src/app/components/AuthTokensList.vue +++ b/packages/self-inspect/src/app/components/AuthTokensList.vue @@ -35,7 +35,7 @@ function formatDate(timestamp: number): string { - Auth ID + Auth Token User Agent diff --git a/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts b/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts index 6e842587..cf4199f9 100644 --- a/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts +++ b/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts @@ -1,17 +1,13 @@ -import { getInternalContext } from '@vitejs/devtools' +import { revokeAuthToken } from '@vitejs/devtools' import { defineRpcFunction } from '@vitejs/devtools-kit' -export const revokeAuthToken = defineRpcFunction({ +export const revokeAuthTokenRpc = defineRpcFunction({ name: 'devtoolskit:self-inspect:revoke-auth-token', type: 'action', setup: (context) => { - const internal = getInternalContext(context) - const storage = internal.storage.auth return { handler: async (authId: string) => { - storage.mutate((state) => { - delete state.trusted[authId] - }) + await revokeAuthToken(context, authId) }, } }, diff --git a/packages/self-inspect/src/node/rpc/index.ts b/packages/self-inspect/src/node/rpc/index.ts index 4e3a1fa1..ad8c8fe1 100644 --- a/packages/self-inspect/src/node/rpc/index.ts +++ b/packages/self-inspect/src/node/rpc/index.ts @@ -4,7 +4,7 @@ import { getClientScripts } from './functions/get-client-scripts' import { getDevtoolsPlugins } from './functions/get-devtools-plugins' import { getDocks } from './functions/get-docks' import { getRpcFunctions } from './functions/get-rpc-functions' -import { revokeAuthToken } from './functions/revoke-auth-token' +import { revokeAuthTokenRpc } from './functions/revoke-auth-token' import '@vitejs/devtools-kit' export const rpcFunctions = [ @@ -13,7 +13,7 @@ export const rpcFunctions = [ getClientScripts, getDevtoolsPlugins, getAuthTokens, - revokeAuthToken, + revokeAuthTokenRpc, ] as const export type ServerFunctions = RpcDefinitionsToFunctions diff --git a/test/exports/@vitejs/devtools.yaml b/test/exports/@vitejs/devtools.yaml index 6a2e0c55..f6cc7502 100644 --- a/test/exports/@vitejs/devtools.yaml +++ b/test/exports/@vitejs/devtools.yaml @@ -3,6 +3,7 @@ createDevToolsMiddleware: function DevTools: function getInternalContext: function + revokeAuthToken: function ./cli-commands: build: function start: function From 8306cccf8974011af14c8753b6e5dda5d377aaa9 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 10:21:05 +0900 Subject: [PATCH 04/16] refactor: move revokeAuthToken to internal context, add @vitejs/devtools/internal export - Attach revokeAuthToken as a method on DevToolsInternalContext instead of a standalone exported function - Create @vitejs/devtools/internal sub-export for getInternalContext - Remove getInternalContext and revokeAuthToken from main entry point - Add alias and tsconfig path for @vitejs/devtools/internal - Update self-inspect to import from @vitejs/devtools/internal Co-Authored-By: Claude Opus 4.6 (1M context) --- alias.ts | 1 + packages/core/package.json | 1 + packages/core/src/index.ts | 2 -- packages/core/src/internal.ts | 2 ++ packages/core/src/node/auth-revoke.ts | 12 ++++++----- packages/core/src/node/context-internal.ts | 20 +++++++++++++------ packages/core/tsdown.config.ts | 1 + .../src/node/rpc/functions/get-auth-tokens.ts | 2 +- .../node/rpc/functions/revoke-auth-token.ts | 5 +++-- test/exports/@vitejs/devtools.yaml | 4 ++-- tsconfig.base.json | 3 +++ 11 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 packages/core/src/internal.ts diff --git a/alias.ts b/alias.ts index 0a0fcb63..7c4d9cd9 100644 --- a/alias.ts +++ b/alias.ts @@ -20,6 +20,7 @@ export const alias = { '@vitejs/devtools-kit': r('kit/src/index.ts'), '@vitejs/devtools-rolldown': r('rolldown/src/index.ts'), '@vitejs/devtools-self-inspect': r('self-inspect/src/index.ts'), + '@vitejs/devtools/internal': r('core/src/internal.ts'), '@vitejs/devtools/client/inject': r('core/src/client/inject/index.ts'), '@vitejs/devtools/client/webcomponents': r('core/src/client/webcomponents/index.ts'), '@vitejs/devtools': r('core/src/index.ts'), diff --git a/packages/core/package.json b/packages/core/package.json index f215f820..7fac76b6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,6 +27,7 @@ "./client/webcomponents": "./dist/client/webcomponents.js", "./config": "./dist/config.js", "./dirs": "./dist/dirs.js", + "./internal": "./dist/internal.js", "./package.json": "./package.json" }, "types": "./dist/index.d.ts", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a439f2e1..68e8d52f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,4 @@ -export { revokeAuthToken } from './node/auth-revoke' export { createDevToolsContext } from './node/context' -export { getInternalContext } from './node/context-internal' export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from './node/context-internal' export { DevTools } from './node/plugins' export { createDevToolsMiddleware } from './node/server' diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts new file mode 100644 index 00000000..72909e95 --- /dev/null +++ b/packages/core/src/internal.ts @@ -0,0 +1,2 @@ +export { getInternalContext } from './node/context-internal' +export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from './node/context-internal' diff --git a/packages/core/src/node/auth-revoke.ts b/packages/core/src/node/auth-revoke.ts index dd803c41..6c0178c8 100644 --- a/packages/core/src/node/auth-revoke.ts +++ b/packages/core/src/node/auth-revoke.ts @@ -1,15 +1,17 @@ import type { DevToolsNodeContext } from '@vitejs/devtools-kit' +import type { SharedState } from '@vitejs/devtools-kit/utils/shared-state' +import type { InternalAnonymousAuthStorage } from './context-internal' import type { RpcFunctionsHost } from './host-functions' -import { getInternalContext } from './context-internal' /** * Revoke an auth token: remove from storage and notify all connected clients * using this token that they are no longer trusted. */ -export async function revokeAuthToken(context: DevToolsNodeContext, token: string): Promise { - const internal = getInternalContext(context) - const storage = internal.storage.auth - +export async function revokeAuthToken( + context: DevToolsNodeContext, + storage: SharedState, + token: string, +): Promise { // Remove from persistent storage storage.mutate((state) => { delete state.trusted[token] diff --git a/packages/core/src/node/context-internal.ts b/packages/core/src/node/context-internal.ts index eb77bb43..66a04435 100644 --- a/packages/core/src/node/context-internal.ts +++ b/packages/core/src/node/context-internal.ts @@ -2,6 +2,7 @@ import type { DevToolsNodeContext } from '@vitejs/devtools-kit' import type { SharedState } from '@vitejs/devtools-kit/utils/shared-state' import { homedir } from 'node:os' import { join } from 'pathe' +import { revokeAuthToken } from './auth-revoke' import { createStorage } from './storage' export interface InternalAnonymousAuthStorage { @@ -17,21 +18,28 @@ export interface DevToolsInternalContext { storage: { auth: SharedState } + /** + * Revoke an auth token: remove from storage and notify all connected clients + * using this token that they are no longer trusted. + */ + revokeAuthToken: (token: string) => Promise } export const internalContextMap = new WeakMap() export function getInternalContext(context: DevToolsNodeContext): DevToolsInternalContext { if (!internalContextMap.has(context)) { + const storage = createStorage({ + filepath: join(homedir(), '.vite/devtools/auth.json'), + initialValue: { + trusted: {}, + }, + }) const internalContext: DevToolsInternalContext = { storage: { - auth: createStorage({ - filepath: join(homedir(), '.vite/devtools/auth.json'), - initialValue: { - trusted: {}, - }, - }), + auth: storage, }, + revokeAuthToken: (token: string) => revokeAuthToken(context, storage, token), } internalContextMap.set(context, internalContext) } diff --git a/packages/core/tsdown.config.ts b/packages/core/tsdown.config.ts index 3280d8d1..55cc03f0 100644 --- a/packages/core/tsdown.config.ts +++ b/packages/core/tsdown.config.ts @@ -48,6 +48,7 @@ export default defineConfig({ tsconfig: '../../tsconfig.base.json', entry: { 'index': 'src/index.ts', + 'internal': 'src/internal.ts', 'dirs': 'src/dirs.ts', 'cli': 'src/node/cli.ts', 'cli-commands': 'src/node/cli-commands.ts', diff --git a/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts b/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts index e5b6bc92..52bd840c 100644 --- a/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts +++ b/packages/self-inspect/src/node/rpc/functions/get-auth-tokens.ts @@ -1,5 +1,5 @@ -import { getInternalContext } from '@vitejs/devtools' import { defineRpcFunction } from '@vitejs/devtools-kit' +import { getInternalContext } from '@vitejs/devtools/internal' export const getAuthTokens = defineRpcFunction({ name: 'devtoolskit:self-inspect:get-auth-tokens', diff --git a/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts b/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts index cf4199f9..cf23b29c 100644 --- a/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts +++ b/packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts @@ -1,13 +1,14 @@ -import { revokeAuthToken } from '@vitejs/devtools' import { defineRpcFunction } from '@vitejs/devtools-kit' +import { getInternalContext } from '@vitejs/devtools/internal' export const revokeAuthTokenRpc = defineRpcFunction({ name: 'devtoolskit:self-inspect:revoke-auth-token', type: 'action', setup: (context) => { + const internal = getInternalContext(context) return { handler: async (authId: string) => { - await revokeAuthToken(context, authId) + await internal.revokeAuthToken(authId) }, } }, diff --git a/test/exports/@vitejs/devtools.yaml b/test/exports/@vitejs/devtools.yaml index f6cc7502..a377097e 100644 --- a/test/exports/@vitejs/devtools.yaml +++ b/test/exports/@vitejs/devtools.yaml @@ -2,8 +2,6 @@ createDevToolsContext: function createDevToolsMiddleware: function DevTools: function - getInternalContext: function - revokeAuthToken: function ./cli-commands: build: function start: function @@ -12,3 +10,5 @@ ./dirs: dirClientStandalone: string dirDist: string +./internal: + getInternalContext: function diff --git a/tsconfig.base.json b/tsconfig.base.json index 7f145b0a..819c2b9a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -51,6 +51,9 @@ "@vitejs/devtools-self-inspect": [ "./packages/self-inspect/src/index.ts" ], + "@vitejs/devtools/internal": [ + "./packages/core/src/internal.ts" + ], "@vitejs/devtools/client/inject": [ "./packages/core/src/client/inject/index.ts" ], From e3c47a1a336ef8f459234a145d277a2c75c516a5 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 10:27:30 +0900 Subject: [PATCH 05/16] feat: close tab and force float mode on auth revocation - When auth is revoked, close the current tab and panel in embedded mode - Force dock mode to 'float' when unauthorized (regardless of settings) - Standalone mode also clears selected entry on revocation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/client/webcomponents/components/dock/Dock.vue | 9 ++++++++- .../webcomponents/components/dock/DockStandalone.vue | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/core/src/client/webcomponents/components/dock/Dock.vue b/packages/core/src/client/webcomponents/components/dock/Dock.vue index 4256c56d..51db4683 100644 --- a/packages/core/src/client/webcomponents/components/dock/Dock.vue +++ b/packages/core/src/client/webcomponents/components/dock/Dock.vue @@ -70,8 +70,15 @@ function onPointerDown(e: PointerEvent) { const isRpcTrusted = ref(context.rpc.isTrusted) context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => { isRpcTrusted.value = isTrusted - if (isTrusted && context.docks.selected?.id === BUILTIN_ENTRY_CLIENT_AUTH_NOTICE.id) + if (isTrusted && context.docks.selected?.id === BUILTIN_ENTRY_CLIENT_AUTH_NOTICE.id) { context.docks.switchEntry(null) + } + else if (!isTrusted) { + // On revocation: close current tab and force float mode + context.docks.switchEntry(null) + context.panel.store.open = false + context.panel.store.mode = 'float' + } }) const groupedEntries = computed(() => context.docks.groupedEntries) diff --git a/packages/core/src/client/webcomponents/components/dock/DockStandalone.vue b/packages/core/src/client/webcomponents/components/dock/DockStandalone.vue index 2a74231c..4eeaf56d 100644 --- a/packages/core/src/client/webcomponents/components/dock/DockStandalone.vue +++ b/packages/core/src/client/webcomponents/components/dock/DockStandalone.vue @@ -20,6 +20,9 @@ const persistedDoms = markRaw(new PersistedDomViewsManager(viewsContainer)) const isRpcTrusted = ref(context.rpc.isTrusted) context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => { isRpcTrusted.value = isTrusted + if (!isTrusted) { + context.docks.switchEntry(null) + } }) watch( From d06781e6b1aa68a8574c4666b9de335667c7d42c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 10:30:20 +0900 Subject: [PATCH 06/16] fix: force float mode when unauthorized without mutating store When unauthorized, DockEmbedded now renders in float mode by checking isRpcTrusted rather than modifying panel.store.mode. This preserves the user's mode preference while ensuring the unauthorized UI always shows in float mode. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/client/webcomponents/components/dock/Dock.vue | 3 +-- .../webcomponents/components/dock/DockEmbedded.vue | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/core/src/client/webcomponents/components/dock/Dock.vue b/packages/core/src/client/webcomponents/components/dock/Dock.vue index 51db4683..069b1f65 100644 --- a/packages/core/src/client/webcomponents/components/dock/Dock.vue +++ b/packages/core/src/client/webcomponents/components/dock/Dock.vue @@ -74,10 +74,9 @@ context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => { context.docks.switchEntry(null) } else if (!isTrusted) { - // On revocation: close current tab and force float mode + // On revocation: close current tab and panel context.docks.switchEntry(null) context.panel.store.open = false - context.panel.store.mode = 'float' } }) diff --git a/packages/core/src/client/webcomponents/components/dock/DockEmbedded.vue b/packages/core/src/client/webcomponents/components/dock/DockEmbedded.vue index d52ad18b..8f4af1cf 100644 --- a/packages/core/src/client/webcomponents/components/dock/DockEmbedded.vue +++ b/packages/core/src/client/webcomponents/components/dock/DockEmbedded.vue @@ -1,7 +1,7 @@ diff --git a/packages/core/src/node/auth-revoke.ts b/packages/core/src/node/auth-revoke.ts index 5040e6e7..dda3d3ce 100644 --- a/packages/core/src/node/auth-revoke.ts +++ b/packages/core/src/node/auth-revoke.ts @@ -24,10 +24,10 @@ export async function revokeAuthToken( // Collect affected session IDs before modifying meta const affectedSessionIds = new Set() for (const client of rpcHost._rpcGroup.clients) { - if (client.$meta.clientAuthId === token) { + if (client.$meta.clientAuthToken === token) { affectedSessionIds.add(client.$meta.id) client.$meta.isTrusted = false - client.$meta.clientAuthId = undefined! + client.$meta.clientAuthToken = undefined! } } diff --git a/packages/core/src/node/auth-state.ts b/packages/core/src/node/auth-state.ts index 4d48972c..1a3f1869 100644 --- a/packages/core/src/node/auth-state.ts +++ b/packages/core/src/node/auth-state.ts @@ -4,7 +4,7 @@ import type { InternalAnonymousAuthStorage } from './context-internal' import { humanId } from '@vitejs/devtools-kit/utils/human-id' export interface PendingAuthRequest { - clientAuthId: string + clientAuthToken: string session: DevToolsNodeRpcSession ua: string origin: string @@ -14,19 +14,19 @@ export interface PendingAuthRequest { } let pendingAuth: PendingAuthRequest | null = null -let tempAuthId: string = generateTempId() +let tempAuthToken: string = generateTempId() function generateTempId(): string { return humanId({ separator: '-', capitalize: false }) } -export function getTempAuthId(): string { - return tempAuthId +export function getTempAuthToken(): string { + return tempAuthToken } -export function refreshTempAuthId(): string { - tempAuthId = generateTempId() - return tempAuthId +export function refreshTempAuthToken(): string { + tempAuthToken = generateTempId() + return tempAuthToken } export function getPendingAuth(): PendingAuthRequest | null { @@ -50,28 +50,28 @@ export function abortPendingAuth(): void { /** * Consume the temp auth ID: verify it matches, trust the pending client, and clean up. - * Returns the client's authId if successful, null otherwise. + * Returns the client's authToken if successful, null otherwise. */ -export function consumeTempAuthId( +export function consumeTempAuthToken( id: string, storage: SharedState, ): string | null { - if (id !== tempAuthId || !pendingAuth) { + if (id !== tempAuthToken || !pendingAuth) { return null } - const { clientAuthId, session, ua, origin, resolve } = pendingAuth + const { clientAuthToken, session, ua, origin, resolve } = pendingAuth // Trust the pending client storage.mutate((state) => { - state.trusted[clientAuthId] = { - authId: clientAuthId, + state.trusted[clientAuthToken] = { + authToken: clientAuthToken, ua, origin, timestamp: Date.now(), } }) - session.meta.clientAuthId = clientAuthId + session.meta.clientAuthToken = clientAuthToken session.meta.isTrusted = true // Resolve the pending auth RPC call @@ -81,7 +81,7 @@ export function consumeTempAuthId( abortPendingAuth() // Generate a new temp ID for next use - refreshTempAuthId() + refreshTempAuthToken() - return clientAuthId + return clientAuthToken } diff --git a/packages/core/src/node/context-internal.ts b/packages/core/src/node/context-internal.ts index 02dfc170..cf19f753 100644 --- a/packages/core/src/node/context-internal.ts +++ b/packages/core/src/node/context-internal.ts @@ -7,7 +7,7 @@ import { createStorage } from './storage' export interface InternalAnonymousAuthStorage { trusted: Record { - state.trusted[query.authId] = { - authId: query.authId, + state.trusted[query.authToken] = { + authToken: query.authToken, ua: query.ua, origin: query.origin, timestamp: Date.now(), } }) - session.meta.clientAuthId = query.authId + session.meta.clientAuthToken = query.authToken session.meta.isTrusted = true - refreshTempAuthId() + refreshTempAuthToken() return { isTrusted: true, } @@ -63,7 +63,7 @@ export const anonymousAuth = defineRpcFunction({ abortPendingAuth() // Generate a fresh temp ID for the auth URL - const tempId = getTempAuthId() + const tempId = getTempAuthToken() // Derive the server URL for the auth link const serverUrl = context.viteServer?.resolvedUrls?.local?.[0]?.replace(/\/$/, '') @@ -75,7 +75,7 @@ export const anonymousAuth = defineRpcFunction({ '', `User Agent : ${c.yellow(c.bold(query.ua || 'Unknown'))}`, `Origin : ${c.yellow(c.bold(query.origin || 'Unknown'))}`, - `Client Token : ${c.green(c.bold(query.authId))}`, + `Client Token : ${c.green(c.bold(query.authToken))}`, '', `Manual Auth URL : ${c.cyan(c.underline(authUrl))}`, `Manual Auth Token : ${c.cyan(c.bold(tempId))}`, @@ -103,13 +103,13 @@ export const anonymousAuth = defineRpcFunction({ const timeout = setTimeout(() => { abortController.abort() setPendingAuth(null) - console.log(c.yellow`${MARK_INFO} Auth request timed out for ${c.bold(query.authId)}`) + console.log(c.yellow`${MARK_INFO} Auth request timed out for ${c.bold(query.authToken)}`) resolve({ isTrusted: false }) }, AUTH_TIMEOUT_MS) // Register as pending auth so auth-verify endpoint can resolve it setPendingAuth({ - clientAuthId: query.authId, + clientAuthToken: query.authToken, session, ua: query.ua, origin: query.origin, @@ -120,7 +120,7 @@ export const anonymousAuth = defineRpcFunction({ // Show terminal confirm prompt with abort signal p.confirm({ - message: c.bold(`Do you trust this client (${c.green(c.bold(query.authId))})?`), + message: c.bold(`Do you trust this client (${c.green(c.bold(query.authToken))})?`), initialValue: false, signal: abortController.signal, }).then((answer) => { @@ -135,21 +135,21 @@ export const anonymousAuth = defineRpcFunction({ if (answer) { storage.mutate((state) => { - state.trusted[query.authId] = { - authId: query.authId, + state.trusted[query.authToken] = { + authToken: query.authToken, ua: query.ua, origin: query.origin, timestamp: Date.now(), } }) - session.meta.clientAuthId = query.authId + session.meta.clientAuthToken = query.authToken session.meta.isTrusted = true - p.outro(c.green(c.bold(`You have granted permissions to ${c.bold(query.authId)}`))) + p.outro(c.green(c.bold(`You have granted permissions to ${c.bold(query.authToken)}`))) resolve({ isTrusted: true }) } else { - p.outro(c.red(c.bold(`You have denied permissions to ${c.bold(query.authId)}`))) + p.outro(c.red(c.bold(`You have denied permissions to ${c.bold(query.authToken)}`))) resolve({ isTrusted: false }) } }).catch(() => { diff --git a/packages/core/src/node/server.ts b/packages/core/src/node/server.ts index 3689dd14..2f4bc68c 100644 --- a/packages/core/src/node/server.ts +++ b/packages/core/src/node/server.ts @@ -3,7 +3,7 @@ import { DEVTOOLS_CONNECTION_META_FILENAME } from '@vitejs/devtools-kit/constant import { createApp, eventHandler, fromNodeMiddleware, getQuery, toNodeListener } from 'h3' import sirv from 'sirv' import { dirClientStandalone } from '../dirs' -import { consumeTempAuthId } from './auth-state' +import { consumeTempAuthToken } from './auth-state' import { getInternalContext } from './context-internal' import { createWsServer } from './ws' @@ -27,20 +27,20 @@ function generateAuthPageHtml(): string { const el = document.getElementById('message') if (!id) { - el.textContent = '\\u26a0\\ufe0f No auth ID found. Please check your URL.' + el.textContent = '\\u26a0\\ufe0f No auth token found. Please check your URL.' el.style.color = '#df513f' } else { fetch(location.pathname.replace(/\\/$/, '') + '-verify?id=' + encodeURIComponent(id)) .then(async (r) => { if (r.status !== 200) throw new Error(await r.text()) const data = await r.json() - const authId = data.authId + const authToken = data.authToken - localStorage.setItem('__VITE_DEVTOOLS_CONNECTION_AUTH_ID__', authId) + localStorage.setItem('__VITE_DEVTOOLS_CONNECTION_AUTH_TOKEN__', authToken) try { const bc = new BroadcastChannel('vite-devtools-auth') - bc.postMessage({ type: 'auth-update', authId: authId }) + bc.postMessage({ type: 'auth-update', authToken: authToken }) } catch {} el.textContent = '\\u2705 Authorized! You can close this window now.' @@ -74,14 +74,14 @@ export async function createDevToolsMiddleware(options: CreateWsServerOptions) { return event.node.res.end('Missing id parameter') } - const clientAuthId = consumeTempAuthId(id, contextInternal.storage.auth) - if (!clientAuthId) { + const clientAuthToken = consumeTempAuthToken(id, contextInternal.storage.auth) + if (!clientAuthToken) { event.node.res.statusCode = 403 - return event.node.res.end('Invalid or expired auth ID') + return event.node.res.end('Invalid or expired auth token') } event.node.res.setHeader('Content-Type', 'application/json') - return event.node.res.end(JSON.stringify({ authId: clientAuthId })) + return event.node.res.end(JSON.stringify({ authToken: clientAuthToken })) })) h3.use('/auth', eventHandler((event) => { diff --git a/packages/core/src/node/ws.ts b/packages/core/src/node/ws.ts index 93b7d037..228bc312 100644 --- a/packages/core/src/node/ws.ts +++ b/packages/core/src/node/ws.ts @@ -52,16 +52,16 @@ export async function createWsServer(options: CreateWsServerOptions) { } else if (authToken && contextInternal.storage.auth.value().trusted[authToken]) { meta.isTrusted = true - meta.clientAuthId = authToken + meta.clientAuthToken = authToken } else if (authToken && ((context.viteConfig.devtools?.config as any)?.clientAuthTokens ?? []).includes(authToken)) { meta.isTrusted = true - meta.clientAuthId = authToken + meta.clientAuthToken = authToken } wsClients.add(ws) const color = meta.isTrusted ? c.green : c.yellow - console.log(color`${MARK_INFO} Websocket client connected. [${meta.id}] [${meta.clientAuthId}] (${meta.isTrusted ? 'trusted' : 'untrusted'})`) + console.log(color`${MARK_INFO} Websocket client connected. [${meta.id}] [${meta.clientAuthToken}] (${meta.isTrusted ? 'trusted' : 'untrusted'})`) }, onDisconnected: (ws, meta) => { wsClients.delete(ws) diff --git a/packages/kit/src/client/rpc-ws.ts b/packages/kit/src/client/rpc-ws.ts index aa6e629c..1ff20890 100644 --- a/packages/kit/src/client/rpc-ws.ts +++ b/packages/kit/src/client/rpc-ws.ts @@ -7,7 +7,7 @@ import { parseUA } from 'ua-parser-modern' import { promiseWithResolver } from '../utils/promise' export interface CreateWsRpcClientModeOptions { - authId: string + authToken: string connectionMeta: ConnectionMeta events: EventEmitter clientRpc: DevToolsClientRpcHost @@ -25,7 +25,7 @@ export function createWsRpcClientMode( options: CreateWsRpcClientModeOptions, ): DevToolsRpcClientMode { const { - authId, + authToken, connectionMeta, events, clientRpc, @@ -44,7 +44,7 @@ export function createWsRpcClientMode( { preset: createWsRpcPreset({ url, - authId, + authToken, ...wsOptions, }), rpcOptions, @@ -76,7 +76,7 @@ export function createWsRpcClientMode( ].filter(i => i).join(' ') const result = await serverRpc.$call('vite:anonymous:auth', { - authId, + authToken, ua, origin: location.origin, }) diff --git a/packages/kit/src/client/rpc.ts b/packages/kit/src/client/rpc.ts index 6a3f601c..3e4fb6db 100644 --- a/packages/kit/src/client/rpc.ts +++ b/packages/kit/src/client/rpc.ts @@ -14,15 +14,15 @@ import { createStaticRpcClientMode } from './rpc-static' import { createWsRpcClientMode } from './rpc-ws' const CONNECTION_META_KEY = '__VITE_DEVTOOLS_CONNECTION_META__' -const CONNECTION_AUTH_ID_KEY = '__VITE_DEVTOOLS_CONNECTION_AUTH_ID__' +const CONNECTION_AUTH_TOKEN_KEY = '__VITE_DEVTOOLS_CONNECTION_AUTH_TOKEN__' export interface DevToolsRpcClientOptions { connectionMeta?: ConnectionMeta baseURL?: string | string[] /** - * The auth id to use for the client + * The auth token to use for the client */ - authId?: string + authToken?: string wsOptions?: Partial rpcOptions?: Partial> } @@ -91,13 +91,13 @@ export interface DevToolsRpcClientMode { callOptional: DevToolsRpcClient['callOptional'] } -function getConnectionAuthIdFromWindows(userAuthId?: string): string { +function getConnectionAuthTokenFromWindows(userAuthToken?: string): string { const getters = [ - () => userAuthId, - () => localStorage.getItem(CONNECTION_AUTH_ID_KEY), - () => (window as any)?.[CONNECTION_AUTH_ID_KEY], - () => (globalThis as any)?.[CONNECTION_AUTH_ID_KEY], - () => (parent.window as any)?.[CONNECTION_AUTH_ID_KEY], + () => userAuthToken, + () => localStorage.getItem(CONNECTION_AUTH_TOKEN_KEY), + () => (window as any)?.[CONNECTION_AUTH_TOKEN_KEY], + () => (globalThis as any)?.[CONNECTION_AUTH_TOKEN_KEY], + () => (parent.window as any)?.[CONNECTION_AUTH_TOKEN_KEY], ] let value: string | undefined @@ -114,8 +114,8 @@ function getConnectionAuthIdFromWindows(userAuthId?: string): string { if (!value) value = humanId({ separator: '-', capitalize: false }) - localStorage.setItem(CONNECTION_AUTH_ID_KEY, value) - ;(globalThis as any)[CONNECTION_AUTH_ID_KEY] = value + localStorage.setItem(CONNECTION_AUTH_TOKEN_KEY, value) + ;(globalThis as any)[CONNECTION_AUTH_TOKEN_KEY] = value return value } @@ -184,7 +184,7 @@ export async function getDevToolsRpcClient( const context: DevToolsClientContext = { rpc: undefined!, } - const authId = getConnectionAuthIdFromWindows(options.authId) + const authToken = getConnectionAuthTokenFromWindows(options.authToken) const clientRpc: DevToolsClientRpcHost = new RpcFunctionsCollectorBase(context) async function fetchJsonFromBases(path: string): Promise { @@ -218,7 +218,7 @@ export async function getDevToolsRpcClient( fetchJsonFromBases, }) : createWsRpcClientMode({ - authId, + authToken, connectionMeta, events, clientRpc, @@ -251,8 +251,8 @@ export async function getDevToolsRpcClient( try { const bc = new BroadcastChannel('vite-devtools-auth') bc.onmessage = (event) => { - if (event.data?.type === 'auth-update' && event.data.authId) { - localStorage.setItem(CONNECTION_AUTH_ID_KEY, event.data.authId) + if (event.data?.type === 'auth-update' && event.data.authToken) { + localStorage.setItem(CONNECTION_AUTH_TOKEN_KEY, event.data.authToken) window.location.reload() } } diff --git a/packages/rpc/src/presets/ws/client.ts b/packages/rpc/src/presets/ws/client.ts index 8bbcb91c..8e45ac21 100644 --- a/packages/rpc/src/presets/ws/client.ts +++ b/packages/rpc/src/presets/ws/client.ts @@ -8,15 +8,15 @@ export interface WebSocketRpcClientOptions { onConnected?: (e: Event) => void onError?: (e: Error) => void onDisconnected?: (e: CloseEvent) => void - authId?: string + authToken?: string } function NOOP() {} export const createWsRpcPreset: RpcClientPreset<(options: WebSocketRpcClientOptions) => ChannelOptions> = defineRpcClientPreset((options: WebSocketRpcClientOptions) => { let url = options.url - if (options.authId) { - url = `${url}?vite_devtools_auth_token=${encodeURIComponent(options.authId)}` + if (options.authToken) { + url = `${url}?vite_devtools_auth_token=${encodeURIComponent(options.authToken)}` } const ws = new WebSocket(url) const { diff --git a/packages/rpc/src/presets/ws/server.ts b/packages/rpc/src/presets/ws/server.ts index 2eed4c66..94933122 100644 --- a/packages/rpc/src/presets/ws/server.ts +++ b/packages/rpc/src/presets/ws/server.ts @@ -11,7 +11,7 @@ import { defineRpcServerPreset } from '..' export interface DevToolsNodeRpcSessionMeta { id: number ws?: WebSocket - clientAuthId?: string + clientAuthToken?: string isTrusted?: boolean subscribedStates: Set } diff --git a/packages/self-inspect/src/app/components/AuthTokensList.vue b/packages/self-inspect/src/app/components/AuthTokensList.vue index c03a486d..5e5441f7 100644 --- a/packages/self-inspect/src/app/components/AuthTokensList.vue +++ b/packages/self-inspect/src/app/components/AuthTokensList.vue @@ -1,6 +1,6 @@ diff --git a/packages/kit/src/client/rpc-static.ts b/packages/kit/src/client/rpc-static.ts index e3305803..7825806a 100644 --- a/packages/kit/src/client/rpc-static.ts +++ b/packages/kit/src/client/rpc-static.ts @@ -15,6 +15,7 @@ export async function createStaticRpcClientMode( return { isTrusted: true, requestTrust: async () => true, + requestTrustWithToken: async () => true, ensureTrusted: async () => true, call: (...args: any): any => staticCaller.call( args[0] as string, diff --git a/packages/kit/src/client/rpc-ws.ts b/packages/kit/src/client/rpc-ws.ts index 1ff20890..f69f8fa8 100644 --- a/packages/kit/src/client/rpc-ws.ts +++ b/packages/kit/src/client/rpc-ws.ts @@ -61,9 +61,10 @@ export function createWsRpcClientMode( }, }) - async function requestTrust() { - if (isTrusted) - return true + let currentAuthToken = authToken + + async function requestTrustWithToken(token: string) { + currentAuthToken = token const info = parseUA(navigator.userAgent) const ua = [ @@ -76,7 +77,7 @@ export function createWsRpcClientMode( ].filter(i => i).join(' ') const result = await serverRpc.$call('vite:anonymous:auth', { - authToken, + authToken: token, ua, origin: location.origin, }) @@ -87,6 +88,12 @@ export function createWsRpcClientMode( return result.isTrusted } + async function requestTrust() { + if (isTrusted) + return true + return requestTrustWithToken(currentAuthToken) + } + async function ensureTrusted(timeout = 60_000): Promise { if (isTrusted) trustedPromise.resolve(true) @@ -113,6 +120,7 @@ export function createWsRpcClientMode( return isTrusted }, requestTrust, + requestTrustWithToken, ensureTrusted, call: (...args: any): any => { return serverRpc.$call( diff --git a/packages/kit/src/client/rpc.ts b/packages/kit/src/client/rpc.ts index 3e4fb6db..70672364 100644 --- a/packages/kit/src/client/rpc.ts +++ b/packages/kit/src/client/rpc.ts @@ -59,6 +59,12 @@ export interface DevToolsRpcClient { */ requestTrust: () => Promise + /** + * Request trust from the server using a specific auth token. + * Updates the stored token and re-requests trust without reloading the page. + */ + requestTrustWithToken: (token: string) => Promise + /** * Call a RPC function on the server */ @@ -86,6 +92,7 @@ export interface DevToolsRpcClientMode { readonly isTrusted: boolean ensureTrusted: DevToolsRpcClient['ensureTrusted'] requestTrust: DevToolsRpcClient['requestTrust'] + requestTrustWithToken: DevToolsRpcClient['requestTrustWithToken'] call: DevToolsRpcClient['call'] callEvent: DevToolsRpcClient['callEvent'] callOptional: DevToolsRpcClient['callOptional'] @@ -234,6 +241,12 @@ export async function getDevToolsRpcClient( connectionMeta, ensureTrusted: mode.ensureTrusted, requestTrust: mode.requestTrust, + requestTrustWithToken: async (token: string) => { + // Update stored token for future reconnections + localStorage.setItem(CONNECTION_AUTH_TOKEN_KEY, token) + ;(globalThis as any)[CONNECTION_AUTH_TOKEN_KEY] = token + return mode.requestTrustWithToken(token) + }, call: mode.call, callEvent: mode.callEvent, callOptional: mode.callOptional, @@ -252,8 +265,7 @@ export async function getDevToolsRpcClient( const bc = new BroadcastChannel('vite-devtools-auth') bc.onmessage = (event) => { if (event.data?.type === 'auth-update' && event.data.authToken) { - localStorage.setItem(CONNECTION_AUTH_TOKEN_KEY, event.data.authToken) - window.location.reload() + rpc.requestTrustWithToken(event.data.authToken) } } } From 329dce8952b2d9c7116b41973b80046b8e4306ef Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 16:33:18 +0900 Subject: [PATCH 14/16] fix: config-based auth tokens should not persist to trusted storage clientAuthTokens from config were being stored in trusted storage on auto-approve, which meant revoking them from self-inspect had no effect since the next auth request would re-match the config and re-store. Now config-based tokens only grant session-level trust (in-memory meta) without persisting to storage. Only terminal-approved and temp-token approved auth tokens are persisted. Also use delete instead of = undefined for immer draft cleanup on revoke. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/core/src/node/auth-revoke.ts | 2 +- packages/core/src/node/rpc/anonymous/auth.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/core/src/node/auth-revoke.ts b/packages/core/src/node/auth-revoke.ts index dda3d3ce..d6aac66c 100644 --- a/packages/core/src/node/auth-revoke.ts +++ b/packages/core/src/node/auth-revoke.ts @@ -14,7 +14,7 @@ export async function revokeAuthToken( ): Promise { // Remove from persistent storage storage.mutate((state) => { - state.trusted[token] = undefined + delete state.trusted[token] }) const rpcHost = context.rpc as unknown as RpcFunctionsHost diff --git a/packages/core/src/node/rpc/anonymous/auth.ts b/packages/core/src/node/rpc/anonymous/auth.ts index 5bcc9765..56177da0 100644 --- a/packages/core/src/node/rpc/anonymous/auth.ts +++ b/packages/core/src/node/rpc/anonymous/auth.ts @@ -40,9 +40,18 @@ export const anonymousAuth = defineRpcFunction({ } } - // Auto-approve if authToken matches a configured auth token or the temp auth ID + // Auto-approve if authToken matches a configured auth token (session-only, not persisted) const tokens = (context.viteConfig.devtools?.config as any)?.clientAuthTokens as string[] ?? [] - if (tokens.includes(query.authToken) || query.authToken === getTempAuthToken()) { + if (tokens.includes(query.authToken)) { + session.meta.clientAuthToken = query.authToken + session.meta.isTrusted = true + return { + isTrusted: true, + } + } + + // Auto-approve if authToken matches the server-generated temp auth token + if (query.authToken === getTempAuthToken()) { storage.mutate((state) => { state.trusted[query.authToken] = { authToken: query.authToken, From 711dfadc9c75bafd3f7fe593d790d79865596e53 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 17:40:08 +0900 Subject: [PATCH 15/16] chore: add debug logging for auth trust diagnostics in WS onConnected Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/core/src/node/ws.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/node/ws.ts b/packages/core/src/node/ws.ts index 228bc312..cd67eef9 100644 --- a/packages/core/src/node/ws.ts +++ b/packages/core/src/node/ws.ts @@ -61,7 +61,8 @@ export async function createWsServer(options: CreateWsServerOptions) { wsClients.add(ws) const color = meta.isTrusted ? c.green : c.yellow - console.log(color`${MARK_INFO} Websocket client connected. [${meta.id}] [${meta.clientAuthToken}] (${meta.isTrusted ? 'trusted' : 'untrusted'})`) + const trustedKeys = Object.keys(contextInternal.storage.auth.value().trusted) + console.log(color`${MARK_INFO} Websocket client connected. [${meta.id}] [${meta.clientAuthToken}] (${meta.isTrusted ? 'trusted' : 'untrusted'}) authToken=${authToken} trustedKeys=${JSON.stringify(trustedKeys)} isClientAuthDisabled=${isClientAuthDisabled}`) }, onDisconnected: (ws, meta) => { wsClients.delete(ws) From 01fe2bdc1bedfa83752561176d972da0031eb4f6 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 19 Mar 2026 17:59:54 +0900 Subject: [PATCH 16/16] chore: fix --- packages/kit/tsdown.config.ts | 1 - pnpm-lock.yaml | 63 ++++------------------------------- 2 files changed, 6 insertions(+), 58 deletions(-) diff --git a/packages/kit/tsdown.config.ts b/packages/kit/tsdown.config.ts index 1e34231f..cc1c1cb0 100644 --- a/packages/kit/tsdown.config.ts +++ b/packages/kit/tsdown.config.ts @@ -22,5 +22,4 @@ export default defineConfig({ 'ua-parser-modern', ], }, - hash: false, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05177328..b00f707f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1041,7 +1041,7 @@ importers: version: 3.5.30(typescript@5.9.3) vue-virtual-scroller: specifier: '>=2.0.0-beta.0' - version: 2.0.0-beta.8(vue@3.5.30(typescript@5.9.3)) + version: 2.0.0-beta.9(vue@3.5.30(typescript@5.9.3)) packages/vite: dependencies: @@ -2619,9 +2619,6 @@ packages: '@poppinss/colors@4.1.6': resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} - '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} - '@poppinss/dumper@0.7.0': resolution: {integrity: sha512-0UTYalzk2t6S4rA2uHOz5bSSW2CHdv4vggJI6Alg90yvl0UgXs6XSXpH96OH+bRkX4J/06djv29pqXJ0lq5Kag==} @@ -4300,9 +4297,6 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001769: - resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} - caniuse-lite@1.0.30001778: resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==} @@ -4364,9 +4358,6 @@ packages: citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - citty@0.2.0: - resolution: {integrity: sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA==} - citty@0.2.1: resolution: {integrity: sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==} @@ -7978,11 +7969,6 @@ packages: peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue-observe-visibility@2.0.0-alpha.1: - resolution: {integrity: sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==} - peerDependencies: - vue: ^3.0.0 - vue-resize@2.0.0-alpha.1: resolution: {integrity: sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==} peerDependencies: @@ -8009,11 +7995,6 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue-virtual-scroller@2.0.0-beta.8: - resolution: {integrity: sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==} - peerDependencies: - vue: ^3.2.0 - vue-virtual-scroller@2.0.0-beta.9: resolution: {integrity: sha512-jSCydaV6ngPQ8NlLhrz5CtnYpe8Hothy7POe5n4gsrsh1F19J5Bp+1zWvtk6igOu3wIqaIU6Utskj+tK8b31gg==} peerDependencies: @@ -8169,9 +8150,6 @@ packages: youch@4.1.0: resolution: {integrity: sha512-cYekNh2tUoU+voS11X0D0UQntVCSO6LQ1h10VriQGmfbpf0mnGTruwZICts23UUNiZCXm8H8hQBtRrdsbhuNNg==} - youch@4.1.0-beta.13: - resolution: {integrity: sha512-3+AG1Xvt+R7M7PSDudhbfbwiyveW6B8PLBIwTyEC598biEYIjHhC89i6DBEvR0EZUjGY3uGSnC429HpIa2Z09g==} - zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -9429,7 +9407,7 @@ snapshots: '@nuxt/telemetry@2.7.0(@nuxt/kit@4.4.2(magicast@0.5.2))': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) - citty: 0.2.0 + citty: 0.2.1 consola: 3.4.2 ofetch: 2.0.0-alpha.3 rc9: 3.0.0 @@ -10020,12 +9998,6 @@ snapshots: dependencies: kleur: 4.1.5 - '@poppinss/dumper@0.6.5': - dependencies: - '@poppinss/colors': 4.1.6 - '@sindresorhus/is': 7.2.0 - supports-color: 10.2.2 - '@poppinss/dumper@0.7.0': dependencies: '@poppinss/colors': 4.1.6 @@ -11994,7 +11966,7 @@ snapshots: browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001769 + caniuse-lite: 1.0.30001778 electron-to-chromium: 1.5.267 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -12065,12 +12037,10 @@ snapshots: caniuse-api@3.0.0: dependencies: browserslist: 4.28.1 - caniuse-lite: 1.0.30001769 + caniuse-lite: 1.0.30001778 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001769: {} - caniuse-lite@1.0.30001778: {} ccount@2.0.1: {} @@ -12124,8 +12094,6 @@ snapshots: dependencies: consola: 3.4.2 - citty@0.2.0: {} - citty@0.2.1: {} clean-regexp@1.0.0: @@ -14269,7 +14237,7 @@ snapshots: unstorage: 1.17.4(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.9.1) untyped: 2.0.0 unwasm: 0.5.3 - youch: 4.1.0-beta.13 + youch: 4.1.0 youch-core: 0.3.3 transitivePeerDependencies: - '@azure/app-configuration' @@ -14732,7 +14700,7 @@ snapshots: nypm@0.6.5: dependencies: - citty: 0.2.0 + citty: 0.2.1 pathe: 2.0.3 tinyexec: 1.0.2 @@ -16685,10 +16653,6 @@ snapshots: transitivePeerDependencies: - supports-color - vue-observe-visibility@2.0.0-alpha.1(vue@3.5.30(typescript@5.9.3)): - dependencies: - vue: 3.5.30(typescript@5.9.3) - vue-resize@2.0.0-alpha.1(vue@3.5.30(typescript@5.9.3)): dependencies: vue: 3.5.30(typescript@5.9.3) @@ -16722,13 +16686,6 @@ snapshots: '@vue/language-core': 3.2.5 typescript: 5.9.3 - vue-virtual-scroller@2.0.0-beta.8(vue@3.5.30(typescript@5.9.3)): - dependencies: - mitt: 2.1.0 - vue: 3.5.30(typescript@5.9.3) - vue-observe-visibility: 2.0.0-alpha.1(vue@3.5.30(typescript@5.9.3)) - vue-resize: 2.0.0-alpha.1(vue@3.5.30(typescript@5.9.3)) - vue-virtual-scroller@2.0.0-beta.9(vue@3.5.30(typescript@5.9.3)): dependencies: mitt: 2.1.0 @@ -16903,14 +16860,6 @@ snapshots: cookie-es: 2.0.0 youch-core: 0.3.3 - youch@4.1.0-beta.13: - dependencies: - '@poppinss/colors': 4.1.6 - '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.14 - cookie-es: 2.0.0 - youch-core: 0.3.3 - zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2