From 301f85db88fe74fbd36089d4dc483841cb334b0c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 28 May 2026 13:00:22 +0900 Subject: [PATCH] feat!: migrate to devframe 0.5.2 and @devframes/hub 0.5.2 Thin @vitejs/devtools-kit to re-export hub primitives (docks, terminals, messages, commands, mountDevframe, json-render) from @devframes/hub. Kit keeps its DevTools* public surface via aliases, so downstream packages need no source changes. Core follows devframe's wire-format renames (hub-internals subpath, devframe:anonymous: RPC scope, devframe_auth_token query param, devframe-auth broadcast channel). SPA mount path stays at /__devtools/. --- AGENTS.md | 29 +- MIGRATION.md | 34 ++ docs/errors/DTK0013.md | 8 +- docs/errors/DTK0050.md | 29 -- docs/errors/DTK0051.md | 27 -- docs/errors/DTK0052.md | 26 - docs/errors/DTK0053.md | 21 - docs/errors/DTK0054.md | 21 - docs/errors/DTK0055.md | 26 - docs/errors/DTK0056.md | 27 -- docs/errors/DTK0057.md | 32 -- docs/errors/index.md | 10 +- docs/kit/remote-client.md | 2 +- packages/core/package.json | 1 + .../client/webcomponents/.generated/css.ts | 2 +- packages/core/src/index.ts | 5 +- packages/core/src/internal.ts | 7 +- packages/core/src/node/plugins/integration.ts | 2 +- packages/core/src/node/rpc/anonymous/auth.ts | 4 +- packages/core/src/node/server.ts | 6 +- packages/core/src/node/ws.ts | 6 +- packages/kit/package.json | 1 + packages/kit/src/client/client-script.ts | 17 +- packages/kit/src/client/context.ts | 3 + packages/kit/src/client/docks.ts | 153 +----- packages/kit/src/client/index.ts | 15 +- packages/kit/src/client/remote.ts | 127 +---- packages/kit/src/constants.ts | 31 +- packages/kit/src/define.ts | 24 +- .../kit/src/node/__tests__/host-docks.test.ts | 445 ------------------ .../src/node/__tests__/host-messages.test.ts | 325 ------------- .../src/node/__tests__/host-terminals.test.ts | 28 -- packages/kit/src/node/context.ts | 118 +---- .../src/node/create-plugin-from-devframe.ts | 45 +- packages/kit/src/node/diagnostics.ts | 36 +- packages/kit/src/node/host-commands.ts | 95 ---- packages/kit/src/node/host-docks.ts | 209 -------- packages/kit/src/node/host-messages.ts | 134 ------ packages/kit/src/node/host-terminals.ts | 201 -------- packages/kit/src/node/index.ts | 16 +- packages/kit/src/node/vite-host.ts | 4 +- packages/kit/src/types/commands.ts | 154 +----- packages/kit/src/types/docks.ts | 198 ++------ packages/kit/src/types/index.ts | 37 +- packages/kit/src/types/json-render.ts | 34 +- packages/kit/src/types/messages.ts | 157 +----- packages/kit/src/types/settings.ts | 12 +- packages/kit/src/types/terminals.ts | 56 +-- packages/kit/src/types/vite-plugin.ts | 8 +- pnpm-lock.yaml | 161 +++++-- pnpm-workspace.yaml | 6 +- .../@vitejs/devtools-kit/client.snapshot.d.ts | 105 +---- .../@vitejs/devtools-kit/client.snapshot.js | 8 +- .../devtools-kit/constants.snapshot.d.ts | 15 +- .../devtools-kit/constants.snapshot.js | 14 +- .../@vitejs/devtools-kit/index.snapshot.d.ts | 14 +- .../@vitejs/devtools-kit/index.snapshot.js | 12 +- .../@vitejs/devtools-kit/node.snapshot.d.ts | 84 +--- .../@vitejs/devtools-kit/node.snapshot.js | 68 +-- 59 files changed, 475 insertions(+), 3020 deletions(-) delete mode 100644 docs/errors/DTK0050.md delete mode 100644 docs/errors/DTK0051.md delete mode 100644 docs/errors/DTK0052.md delete mode 100644 docs/errors/DTK0053.md delete mode 100644 docs/errors/DTK0054.md delete mode 100644 docs/errors/DTK0055.md delete mode 100644 docs/errors/DTK0056.md delete mode 100644 docs/errors/DTK0057.md delete mode 100644 packages/kit/src/node/__tests__/host-docks.test.ts delete mode 100644 packages/kit/src/node/__tests__/host-messages.test.ts delete mode 100644 packages/kit/src/node/__tests__/host-terminals.test.ts delete mode 100644 packages/kit/src/node/host-commands.ts delete mode 100644 packages/kit/src/node/host-docks.ts delete mode 100644 packages/kit/src/node/host-messages.ts delete mode 100644 packages/kit/src/node/host-terminals.ts diff --git a/AGENTS.md b/AGENTS.md index c2299b6f..3d1b2191 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,12 +2,13 @@ ## Positioning -Two layers, one mental model: +Three layers, one mental model: - **`devframe`** — *the container for one devtool integration, portable across viewers.* External project; lives at [`github.com/devframes/devframe`](https://github.com/devframes/devframe), docs at [`devfra.me`](https://devfra.me). Consumed here as an npm dependency (`catalog:deps`). -- **`@vitejs/devtools-kit`** — *the hub that unites many devtools integrations.* Owns docking, the command palette, toasts, terminal sessions — anything that only makes sense when more than one tool shares a UI. Provides `createPluginFromDevframe(devframeApp)` so a portable devframe definition drops into Vite DevTools as a Vite plugin, with the dock entry auto-derived from the definition's metadata. +- **`@devframes/hub`** — *the framework-neutral hub layer on top of devframe.* Owns docks, terminals, messages, commands, the `mountDevframe` primitive, and the json-render factory — anything that only matters once a host wants to combine multiple devframes into one UI. External project, same repo as devframe; consumed via npm. +- **`@vitejs/devtools-kit`** — *the Vite-flavored skin over `@devframes/hub`.* Re-exports hub's hosts and primitives under the kit's `DevTools*` names, adds the Vite-specific extensions (`ViteDevToolsNodeContext`, `PluginWithDevTools`, `DevToolsPluginOptions`, `createViteDevToolsHost`, the `~viteplus` dock category), pins the kit-side mount path at `/__devtools/`, and ships `createPluginFromDevframe` to drop a portable devframe into Vite DevTools as a Vite plugin. -When deciding where something belongs: if a single-app standalone CLI would still need it, it belongs upstream in devframe; if it only matters once you have multiple integrations or a host UI, it lives in the kit. +When deciding where something belongs: if a single-app standalone CLI would still need it, it belongs upstream in devframe; if it only matters once a host combines multiple integrations, it belongs in `@devframes/hub` (or in `@vitejs/devtools-kit` if it's Vite-specific). ## Stack & Structure @@ -17,7 +18,7 @@ Monorepo (`pnpm` workspaces + `turbo`). ESM TypeScript; bundled with `tsdown`. P | Package | npm | Description | |---------|-----|-------------| -| `packages/kit` | `@vitejs/devtools-kit` | The hub. `createKitContext` wraps devframe's context with `docks` / `terminals` / `messages` / `commands` host subsystems plus the Vite-augmented context type. `createPluginFromDevframe` bridges a portable devframe app into a `Plugin.devtools.setup` Vite plugin, auto-deriving its iframe dock entry from the definition. | +| `packages/kit` | `@vitejs/devtools-kit` | Vite-flavored skin over `@devframes/hub`. `createKitContext` wraps hub's `createHubContext` and surfaces the Vite-augmented context type (`viteConfig`/`viteServer`); hub hosts (`docks` / `terminals` / `messages` / `commands`) and the `mountDevframe` primitive are re-exported under the kit's `DevTools*` aliases. `createPluginFromDevframe` delegates to `mountDevframe` and wraps it in a `Plugin.devtools.setup` Vite plugin shell. | | `packages/core` | `@vitejs/devtools` | Vite plugin + CLI + standalone/webcomponents client for Vite DevTools itself. Calls kit's `createKitContext`, scans Vite plugins for `.devtools.setup`, and serves the dock UI. | | `packages/ui` | `@vitejs/devtools-ui` | Shared UI components, composables, and UnoCSS preset (`presetDevToolsUI`). Private, not published. | | `packages/rolldown` | `@vitejs/devtools-rolldown` | Nuxt UI for Rolldown build data. Hub-mounted via `Plugin.devtools.setup`. Serves at `/__devtools-rolldown/`. | @@ -31,7 +32,8 @@ Other top-level directories: ```mermaid flowchart TD - kit --> devframe + hub --> devframe + kit --> hub core --> kit core --> rolldown & vite & self-inspect rolldown --> kit & ui @@ -42,15 +44,16 @@ flowchart TD ## Dep Boundary -`devframe` is an external package consumed via `catalog:deps` — contribute upstream at [github.com/devframes/devframe](https://github.com/devframes/devframe). `packages/kit` and above build on top of it. Features that require multi-integration awareness (docks, terminals, messages, commands) belong in kit. +`devframe` and `@devframes/hub` are external packages consumed via `catalog:deps` — contribute upstream at [github.com/devframes/devframe](https://github.com/devframes/devframe). `packages/kit` and above build on top of them. Features that require multi-integration awareness (docks, terminals, messages, commands) belong upstream in `@devframes/hub`. Features that only matter to Vite — `ViteDevToolsNodeContext`, `PluginWithDevTools`, the `~viteplus` category, the kit-pinned `/__devtools/` mount path, the `vite:open-in-editor`/`vite:open-in-finder` commands — stay in `@vitejs/devtools-kit` and `@vitejs/devtools`. -`devframe/node/internal` is a marked-internal subpath exposing a small set of helpers (`getInternalContext`, `resolveBasePath`) for first-party adapters reaching into devframe's private machinery — kit's `DocksHost` uses it for remote-dock token allocation. End users should not import it. +`devframe/node/hub-internals` is a marked-public-but-low-level subpath exposing a small set of helpers (`getInternalContext`, `resolveBasePath`) for first-party adapters reaching into devframe's hub-side machinery — kit's adapters use `getInternalContext` for remote-dock token allocation and WS-endpoint metadata. End users should not import it. ## Architecture -- **Devframe context** (external — see [devfra.me](https://devfra.me)): `createHostContext` returns a `DevToolsNodeContext` carrying `rpc`, `views` (HTTP file-serving via `hostStatic`), `diagnostics`, `agent`, plus `cwd`/`workspaceRoot`/`mode`/`host`. No docks, no terminals, no json-render. -- **Kit context** (`packages/kit/src/node/context.ts`): `createKitContext` wraps `createHostContext` and attaches the four hub hosts — `docks`, `terminals`, `messages`, `commands` — plus the `createJsonRenderer` factory. Optionally surfaces `viteConfig`/`viteServer` when mounted inside Vite DevTools. Wires the `'devframe:docks'` / `'devframe:commands'` shared-state sync. -- **Bridge** (`packages/kit/src/node/create-plugin-from-devframe.ts`): `createPluginFromDevframe(d, opts?)` returns `PluginWithDevTools`; in its `setup`, mounts the SPA via `views.hostStatic`, auto-registers an iframe dock entry from `id`/`name`/`icon`/`basePath`, runs `d.setup(ctx)` for the devframe-level wiring, then runs `opts.setup?.(ctx)` for kit-only extensions. +- **Devframe context** (external — see [devfra.me](https://devfra.me)): `createHostContext` returns a `DevframeNodeContext` carrying `rpc`, `views` (HTTP file-serving via `hostStatic`), `diagnostics`, `agent`, plus `cwd`/`workspaceRoot`/`mode`/`host`. No docks, no terminals, no json-render. +- **Hub context** (external `@devframes/hub/node`): `createHubContext` wraps `createHostContext` and attaches the four hub hosts — `docks`, `terminals`, `messages`, `commands` — plus the `createJsonRenderer` factory. Wires the `'devframe:docks'` / `'devframe:commands'` shared-state sync and seeds the built-in `~terminals` / `~messages` / `~settings` docks. Also ships `mountDevframe(ctx, def)` — the framework-neutral primitive that registers any `DevframeDefinition` as a dock. +- **Kit context** (`packages/kit/src/node/context.ts`): `createKitContext` wraps `createHubContext` and surfaces Vite-specific `viteConfig`/`viteServer` slots when mounted inside Vite DevTools. `KitNodeContext` extends `DevframeHubContext` so all the hub hosts come along for free. +- **Bridge** (`packages/kit/src/node/create-plugin-from-devframe.ts`): `createPluginFromDevframe(d, opts?)` returns `PluginWithDevTools`; in its `setup`, delegates to `mountDevframe(ctx, d, opts)` to mount the SPA, register the auto-derived iframe dock entry, and run `d.setup(ctx)`, then runs `opts.setup?.(ctx)` for kit-only extensions. - **Vite DevTools entry** (`packages/core/src/node/context.ts`): `createDevToolsContext` calls `createKitContext`, registers Vite-specific commands (`vite:open-in-editor`, `vite:open-in-finder`), then scans Vite plugins for `.devtools.setup` hooks (which now receive the kit-augmented context). - **Client context**: webcomponents/Nuxt UI state (`packages/core/src/client/webcomponents/state/*`) — dock entries, panels, RPC client. Two modes: `embedded` (overlay in host app) and `standalone` (independent page). - **WS server** (`packages/core/src/node/ws.ts`): RPC via `devframe/rpc/transports/ws-server`. Auth skipped in build mode or when `devtools.clientAuth` is `false`. @@ -97,13 +100,13 @@ All node-side warnings and errors use structured diagnostics via [`nostics`](htt | Prefix | Package(s) | Diagnostics file | |--------|-----------|-----------------| -| `DTK` | `packages/kit` + `packages/core` (shared codespace, hub-side) | `packages/kit/src/node/diagnostics.ts`, `packages/core/src/node/diagnostics.ts` | +| `DTK` | `packages/kit` + `packages/core` (shared codespace, Vite-side) | `packages/kit/src/node/diagnostics.ts`, `packages/core/src/node/diagnostics.ts` | | `RDDT` | `packages/rolldown` | `packages/rolldown/src/node/diagnostics.ts` | | `VDT` | `packages/vite` (reserved) | — | -`DF` codes belong to the upstream devframe project — file new ones there. +`DF` codes belong to the upstream devframe/hub projects — file new ones there. The `DF8xxx` sub-range covers `@devframes/hub` (DF8100–DF8199 docks, DF8200–DF8299 terminals, DF8300–DF8399 messages, DF8400–DF8499 commands). -`DTK` is shared between core and kit because they're sibling layers of the Vite DevTools hub. Coordinate code numbers across both files: kit currently reserves `DTK0050+`; core's existing codes top out below that. +`DTK` is shared between core and kit because they're sibling layers of Vite DevTools. Coordinate code numbers across both files: kit reserves `DTK0050+` for Vite-specific kit codes; core's existing codes top out below that. The hub-domain DTK codes (DTK0050–DTK0057) retired when their conditions moved upstream to `DF8100`–`DF8403`. Codes are sequential 4-digit numbers per prefix (e.g. `DTK0033`, `RDDT0003`). Check the existing diagnostics file to find the next available number. diff --git a/MIGRATION.md b/MIGRATION.md index eca20944..73c5379a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,39 @@ # Migration Guide +## 0.2 → 0.3 (devframe 0.5.x + `@devframes/hub`) + +`@vitejs/devtools-kit` now thins onto [`@devframes/hub`](https://www.npmjs.com/package/@devframes/hub) for the hub primitives (docks, terminals, messages, commands, `mountDevframe`, json-render factory). The kit's public `DevTools*` surface stays in place via re-export aliases — most plugin authors don't need to change anything. + +### What changes for plugin authors + +- **Nothing at the import level.** `DevToolsRpcClient`, `DevToolsDockEntry`, `DevToolsNodeContext`, `PluginWithDevTools`, `defineRpcFunction`, `getDevToolsRpcClient`, `connectRemoteDevTools`, the `DEVTOOLS_MOUNT_PATH` constant — all keep their `@vitejs/devtools-kit` import paths and identifier names. +- **One-time re-auth.** The anonymous-auth RPC scope moved from `vite:anonymous:` to `devframe:anonymous:` and the WebSocket auth-token query parameter from `vite_devtools_auth_token` to `devframe_auth_token` (both following devframe's internal rename). Auth tokens stored by older clients become invalid — users re-authorize once on first connect after upgrading. +- **`DTK0050`–`DTK0057` retire.** These dock/terminal/command diagnostic codes now ship from `@devframes/hub` as `DF8100`–`DF8403`. Error messages and docs URLs change accordingly; the conditions that trigger them are unchanged. + +### What stays the same + +- The Vite DevTools SPA continues to serve at **`/__devtools/`**. The kit pins this mount path independently of devframe's new `/__devframe/` default. +- `~viteplus` remains a valid dock category. +- `vite:open-in-editor` and `vite:open-in-finder` server commands keep their existing IDs. + +### If you import from `devframe` directly + +The `devframe/node/internal` subpath was renamed to `devframe/node/hub-internals` in devframe v0.5.x. Update direct imports: + +```diff +- import { getInternalContext } from 'devframe/node/internal' ++ import { getInternalContext } from 'devframe/node/hub-internals' +``` + +And the type rename: + +```diff +- import type { DevToolsInternalContext } from 'devframe/node/internal' ++ import type { DevframeInternalContext } from 'devframe/node/hub-internals' +``` + +Inside the kit's own re-export (`@vitejs/devtools/internal`), the alias `DevToolsInternalContext` remains. + ## 0.1 → 0.2 ### `@vitejs/devtools-kit` diff --git a/docs/errors/DTK0013.md b/docs/errors/DTK0013.md index 0d7e929c..6fed2dc5 100644 --- a/docs/errors/DTK0013.md +++ b/docs/errors/DTK0013.md @@ -10,12 +10,12 @@ outline: deep ## Cause -This error is thrown by the RPC resolver in `createWsServer()` when an untrusted WebSocket client attempts to call a non-anonymous RPC method. Methods whose names start with `vite:anonymous:` are exempt from authentication and can be called by any client; all other methods require the client to be trusted. +This error is thrown by the RPC resolver in `createWsServer()` when an untrusted WebSocket client attempts to call a non-anonymous RPC method. Methods whose names start with `devframe:anonymous:` are exempt from authentication and can be called by any client; all other methods require the client to be trusted. A client becomes trusted through one of these mechanisms: 1. **Client auth is disabled** (build mode, `clientAuth: false`, or `VITE_DEVTOOLS_DISABLE_CLIENT_AUTH=true`) -- all clients are auto-trusted. -2. **Auth token in storage** -- the client provides a `vite_devtools_auth_token` query parameter that matches a token stored in `node_modules/.vite/devtools/auth.json`. +2. **Auth token in storage** -- the client provides a `devframe_auth_token` query parameter that matches a token stored in `node_modules/.vite/devtools/auth.json`. 3. **Static auth tokens** -- the token matches one of the tokens listed in `devtools.config.clientAuthTokens`. If none of these conditions are met, the client is untrusted and any call to a non-anonymous method triggers this error. @@ -41,7 +41,7 @@ Provide a valid authentication token when connecting: ```ts // Client-side: connecting with a valid auth token const ws = new WebSocket( - 'ws://localhost:7812?vite_devtools_auth_token=your-token-here' + 'ws://localhost:7812?devframe_auth_token=your-token-here' ) ``` @@ -67,4 +67,4 @@ If you are developing locally and want to skip authentication entirely, see [DTK ## Source -- [`packages/core/src/node/ws.ts`](https://github.com/vitejs/devtools/blob/main/packages/core/src/node/ws.ts) — the WebSocket RPC resolver in `createWsServer()` throws `DTK0013` when an untrusted client invokes a method whose name is not prefixed with `vite:anonymous:`. +- [`packages/core/src/node/ws.ts`](https://github.com/vitejs/devtools/blob/main/packages/core/src/node/ws.ts) — the WebSocket RPC resolver in `createWsServer()` throws `DTK0013` when an untrusted client invokes a method whose name is not prefixed with `devframe:anonymous:`. diff --git a/docs/errors/DTK0050.md b/docs/errors/DTK0050.md deleted file mode 100644 index d99c5b51..00000000 --- a/docs/errors/DTK0050.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -outline: deep ---- - -# DTK0050: Dock Already Registered - -## Message - -> Dock with id "`{id}`" is already registered - -## Cause - -`ctx.docks.register(view)` is called twice with the same `id`. The kit's `DocksHost` enforces unique ids so each dock entry has a stable handle. - -## Example - -```ts -ctx.docks.register({ id: 'my-plugin:main', title: 'My Plugin', type: 'iframe', url: '/__foo/' }) -ctx.docks.register({ id: 'my-plugin:main', title: 'My Plugin (again)', type: 'iframe', url: '/__foo/' }) -// Throws DTK0050 -``` - -## Fix - -Pick a different `id`, or call `ctx.docks.update(...)` to mutate the existing entry. Pass `force: true` as the second arg to `register` to intentionally replace the existing entry — this is what the auto-derived dock from `createPluginFromDevframe` uses internally. - -## Source - -- [`packages/kit/src/node/host-docks.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-docks.ts) — `DevToolsDockHost.register` throws `DTK0050` when an id is already in `views` and `force` was not set. diff --git a/docs/errors/DTK0051.md b/docs/errors/DTK0051.md deleted file mode 100644 index d42ced5f..00000000 --- a/docs/errors/DTK0051.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -outline: deep ---- - -# DTK0051: Cannot Change Dock ID - -## Message - -> Cannot change the id of a dock. Use register() to add new docks. - -## Cause - -The handle returned by `ctx.docks.register({ id })` exposes an `update(patch)` method. Passing a different `id` in that patch is rejected — ids identify the entry and must remain stable for the kit client to track it across reactivity updates. - -## Fix - -To "rename" a dock, call `ctx.docks.register(...)` with the new id. To change other fields, leave `id` out of the patch: - -```ts -const handle = ctx.docks.register({ id: 'my-plugin', title: 'Old', /* ... */ }) -handle.update({ title: 'New' }) // ✓ -handle.update({ id: 'renamed' }) // ✗ DTK0051 -``` - -## Source - -- [`packages/kit/src/node/host-docks.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-docks.ts) — the `update` closure returned from `register` throws `DTK0051` when the patch carries a different `id`. diff --git a/docs/errors/DTK0052.md b/docs/errors/DTK0052.md deleted file mode 100644 index 3cb2495c..00000000 --- a/docs/errors/DTK0052.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -outline: deep ---- - -# DTK0052: Dock Not Registered - -## Message - -> Dock with id "`{id}`" is not registered. Use register() to add new docks. - -## Cause - -`ctx.docks.update(view)` was called with an `id` that has not been registered. Updates require the dock to exist first. - -## Fix - -Call `ctx.docks.register(...)` before `update`, or guard with `ctx.docks.views.has(id)`: - -```ts -if (ctx.docks.views.has('my-plugin')) - ctx.docks.update({ id: 'my-plugin', badge: '3' }) -``` - -## Source - -- [`packages/kit/src/node/host-docks.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-docks.ts) — `DevToolsDockHost.update` throws `DTK0052` when the supplied entry's id isn't in `views`. diff --git a/docs/errors/DTK0053.md b/docs/errors/DTK0053.md deleted file mode 100644 index f59cb7f1..00000000 --- a/docs/errors/DTK0053.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -outline: deep ---- - -# DTK0053: Terminal Session Already Registered - -## Message - -> Terminal session with id "`{id}`" already registered - -## Cause - -`ctx.terminals.register(session)` (or the `startChildProcess` shortcut) was called with an `id` that already exists in the session map. - -## Fix - -Pick a unique id for each session, or call `ctx.terminals.remove(...)` on the existing one before registering a replacement. For long-running processes, prefer `session.restart()` over re-registering. - -## Source - -- [`packages/kit/src/node/host-terminals.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-terminals.ts) — both `DevToolsTerminalHost.register` and `startChildProcess` throw `DTK0053` when `sessions` already has the id. diff --git a/docs/errors/DTK0054.md b/docs/errors/DTK0054.md deleted file mode 100644 index d33ddadf..00000000 --- a/docs/errors/DTK0054.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -outline: deep ---- - -# DTK0054: Terminal Session Not Registered - -## Message - -> Terminal session with id "`{id}`" not registered - -## Cause - -`ctx.terminals.update(patch)` was called with an `id` that has no corresponding session. Updates can only target a session that is still alive in the kit's terminal map. - -## Fix - -Register the session via `ctx.terminals.register(...)` (or `ctx.terminals.startChildProcess(...)`) first. Guard updates with `ctx.terminals.sessions.has(id)` if a session may have been removed concurrently. - -## Source - -- [`packages/kit/src/node/host-terminals.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-terminals.ts) — `DevToolsTerminalHost.update` throws `DTK0054` when the patch's id isn't in `sessions`. diff --git a/docs/errors/DTK0055.md b/docs/errors/DTK0055.md deleted file mode 100644 index 491acf0e..00000000 --- a/docs/errors/DTK0055.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -outline: deep ---- - -# DTK0055: Command Already Registered - -## Message - -> Command "`{id}`" is already registered - -## Cause - -`ctx.commands.register(command)` was called twice with the same `id`. Command ids must be globally unique across all registered integrations because the palette merges them by id. - -## Fix - -Pick a different namespaced id (e.g. `my-plugin:do-thing`), or use the handle returned from the original `register` call to mutate the existing command: - -```ts -const handle = ctx.commands.register({ id: 'my-plugin:open', title: 'Open', handler: () => {} }) -handle.update({ title: 'Open File' }) -``` - -## Source - -- [`packages/kit/src/node/host-commands.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-commands.ts) — `DevToolsCommandsHost.register` throws `DTK0055` when `commands` already has the id. diff --git a/docs/errors/DTK0056.md b/docs/errors/DTK0056.md deleted file mode 100644 index 4a2fe290..00000000 --- a/docs/errors/DTK0056.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -outline: deep ---- - -# DTK0056: Cannot Change Command ID - -## Message - -> Cannot change the id of a command. Use register() to add new commands. - -## Cause - -The handle returned by `ctx.commands.register(...)` exposes an `update(patch)` method that intentionally rejects an `id` field — palette state, keybinding overrides, and the palette renderer all key off the id. - -## Fix - -To rename a command, unregister the old one and register a new one. Otherwise, omit `id` from the patch: - -```ts -const handle = ctx.commands.register({ id: 'my-plugin:run', title: 'Run', handler }) -handle.update({ title: 'Run again' }) // ✓ -handle.update({ id: 'renamed' }) // ✗ DTK0056 -``` - -## Source - -- [`packages/kit/src/node/host-commands.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-commands.ts) — the `update` closure returned from `register` throws `DTK0056` when the patch carries an `id` field. diff --git a/docs/errors/DTK0057.md b/docs/errors/DTK0057.md deleted file mode 100644 index 02189ec1..00000000 --- a/docs/errors/DTK0057.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -outline: deep ---- - -# DTK0057: Command Not Registered - -## Message - -> Command "`{id}`" is not registered - -## Cause - -The kit's commands host throws this error from two paths: - -1. The handle's `update(patch)` was called after the underlying command was already unregistered. -2. `ctx.commands.execute(id, ...args)` was called with an id that doesn't match any registered command (top-level or child). - -## Fix - -Verify the id exists before executing: - -```ts -const cmd = ctx.commands.commands.get('my-plugin:run') -if (cmd?.handler) - await ctx.commands.execute('my-plugin:run') -``` - -For palette executions surfaced via the client, this typically indicates a race between the client palette UI and a server-side `unregister`. Re-render the palette after the next `command:registered` / `command:unregistered` event. - -## Source - -- [`packages/kit/src/node/host-commands.ts`](https://github.com/vitejs/devtools/blob/main/packages/kit/src/node/host-commands.ts) — `execute` and the `update` closure both throw `DTK0057` when the command id can't be resolved via `findCommand` / `commands.get`. diff --git a/docs/errors/index.md b/docs/errors/index.md index 54660d62..10004303 100644 --- a/docs/errors/index.md +++ b/docs/errors/index.md @@ -31,14 +31,8 @@ Emitted by `@vitejs/devtools` and `@vitejs/devtools-kit`. | [DTK0030](./DTK0030) | error | Dock Entry Not Found | | [DTK0031](./DTK0031) | error | Dock Entry Not a Launcher | | [DTK0032](./DTK0032) | error | Dock Launch Error | -| [DTK0050](./DTK0050) | error | Dock Already Registered | -| [DTK0051](./DTK0051) | error | Cannot Change Dock ID | -| [DTK0052](./DTK0052) | error | Dock Not Registered | -| [DTK0053](./DTK0053) | error | Terminal Session Already Registered | -| [DTK0054](./DTK0054) | error | Terminal Session Not Registered | -| [DTK0055](./DTK0055) | error | Command Already Registered | -| [DTK0056](./DTK0056) | error | Cannot Change Command ID | -| [DTK0057](./DTK0057) | error | Command Not Registered | + +Hub-side diagnostics for docks, terminals, messages, and commands live upstream in `@devframes/hub` under the `DF8xxx` range — see the [Devframe error reference](https://devfra.me/errors/). ## Rolldown DevTools (RDDT) diff --git a/docs/kit/remote-client.md b/docs/kit/remote-client.md index 9181d800..8a4fce25 100644 --- a/docs/kit/remote-client.md +++ b/docs/kit/remote-client.md @@ -35,7 +35,7 @@ sequenceDiagram Core->>Core: createWsServer → wsEndpoint=ws://localhost:7812 User->>Core: open dock Core->>Hosted: iframe src + connection descriptor - Hosted->>Core: WS connect (?vite_devtools_auth_token=...) + Hosted->>Core: WS connect (?devframe_auth_token=...) Core->>Core: verify token + Origin Core-->>Hosted: trusted session Hosted->>Core: rpc.call('my-plugin:…') diff --git a/packages/core/package.json b/packages/core/package.json index b2592739..fb93da51 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -56,6 +56,7 @@ "vite": "*" }, "dependencies": { + "@devframes/hub": "catalog:deps", "@vitejs/devtools-kit": "workspace:*", "@vitejs/devtools-rolldown": "workspace:*", "birpc": "catalog:deps", diff --git a/packages/core/src/client/webcomponents/.generated/css.ts b/packages/core/src/client/webcomponents/.generated/css.ts index c0555411..bd808e89 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-right{--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='m181.66 133.66l-80 80a8 8 0 0 1-11.32-11.32L164.69 128L90.34 53.66a8 8 0 0 1 11.32-11.32l80 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-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-dots-six-vertical{--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='M104 60a12 12 0 1 1-12-12a12 12 0 0 1 12 12m60 12a12 12 0 1 0-12-12a12 12 0 0 0 12 12m-72 44a12 12 0 1 0 12 12a12 12 0 0 0-12-12m72 0a12 12 0 1 0 12 12a12 12 0 0 0-12-12m-72 68a12 12 0 1 0 12 12a12 12 0 0 0-12-12m72 0a12 12 0 1 0 12 12a12 12 0 0 0-12-12'/%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-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-keyboard-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 64v128a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8V64a8 8 0 0 1 8-8h192a8 8 0 0 1 8 8' opacity='.2'/%3E%3Cpath d='M224 48H32a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h192a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16m0 144H32V64h192zm-16-64a8 8 0 0 1-8 8H56a8 8 0 0 1 0-16h144a8 8 0 0 1 8 8m0-32a8 8 0 0 1-8 8H56a8 8 0 0 1 0-16h144a8 8 0 0 1 8 8M72 160a8 8 0 0 1-8 8h-8a8 8 0 0 1 0-16h8a8 8 0 0 1 8 8m96 0a8 8 0 0 1-8 8H96a8 8 0 0 1 0-16h64a8 8 0 0 1 8 8m40 0a8 8 0 0 1-8 8h-8a8 8 0 0 1 0-16h8a8 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-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-magnifying-glass-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='M192 112a80 80 0 1 1-80-80a80 80 0 0 1 80 80' opacity='.2'/%3E%3Cpath d='m229.66 218.34l-50.06-50.06a88.21 88.21 0 1 0-11.32 11.31l50.06 50.07a8 8 0 0 0 11.32-11.32M40 112a72 72 0 1 1 72 72a72.08 72.08 0 0 1-72-72'/%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-paint-brush-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 32c0 32.81-31.64 67.43-58.64 91.05A84.4 84.4 0 0 0 133 90.64c23.57-27 58.19-58.64 91-58.64' opacity='.2'/%3E%3Cpath d='M232 32a8 8 0 0 0-8-8c-44.08 0-89.31 49.71-114.43 82.63A60 60 0 0 0 32 164c0 30.88-19.54 44.73-20.47 45.37A8 8 0 0 0 16 224h76a60 60 0 0 0 57.37-77.57C182.3 121.31 232 76.08 232 32M92 208H34.63C41.38 198.41 48 183.92 48 164a44 44 0 1 1 44 44m32.42-94.45q5.14-6.66 10.09-12.55A76.2 76.2 0 0 1 155 121.49q-5.9 4.94-12.55 10.09a60.5 60.5 0 0 0-18.03-18.03m42.7-2.68a92.6 92.6 0 0 0-22-22c31.78-34.53 55.75-45 69.9-47.91c-2.85 14.16-13.37 38.13-47.9 69.91'/%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-spinner-gap-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='M136 32v32a8 8 0 0 1-16 0V32a8 8 0 0 1 16 0m88 88h-32a8 8 0 0 0 0 16h32a8 8 0 0 0 0-16m-45.09 47.6a8 8 0 0 0-11.31 11.31l22.62 22.63a8 8 0 0 0 11.32-11.32ZM128 184a8 8 0 0 0-8 8v32a8 8 0 0 0 16 0v-32a8 8 0 0 0-8-8m-50.91-16.4l-22.63 22.62a8 8 0 0 0 11.32 11.32l22.62-22.63a8 8 0 0 0-11.31-11.31M72 128a8 8 0 0 0-8-8H32a8 8 0 0 0 0 16h32a8 8 0 0 0 8-8m-6.22-73.54a8 8 0 0 0-11.32 11.32L77.09 88.4A8 8 0 0 0 88.4 77.09Z'/%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-wrench-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 96a64 64 0 0 1-94.94 56L73 217a24 24 0 0 1-34-34l65-56.06a64 64 0 0 1 80-90.29L144 80l5.66 26.34L176 112l43.35-40A63.8 63.8 0 0 1 224 96' opacity='.2'/%3E%3Cpath d='M226.76 69a8 8 0 0 0-12.84-2.88l-40.3 37.19l-17.23-3.7l-3.7-17.23l37.19-40.3A8 8 0 0 0 187 29.24A72 72 0 0 0 88 96a72.3 72.3 0 0 0 6 28.94L33.79 177c-.15.12-.29.26-.43.39a32 32 0 0 0 45.26 45.26c.13-.13.27-.28.39-.42L131.06 162A72 72 0 0 0 232 96a71.6 71.6 0 0 0-5.24-27M160 152a56.14 56.14 0 0 1-27.07-7a8 8 0 0 0-9.92 1.77l-55.9 64.74a16 16 0 0 1-22.62-22.62L109.18 133a8 8 0 0 0 1.77-9.93a56 56 0 0 1 58.36-82.31l-31.2 33.81a8 8 0 0 0-1.94 7.1l5.66 26.33a8 8 0 0 0 6.14 6.14l26.35 5.66a8 8 0 0 0 7.1-1.94l33.81-31.2A56.06 56.06 0 0 1 160 152'/%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{--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='m229.66 77.66l-128 128a8 8 0 0 1-11.32 0l-56-56a8 8 0 0 1 11.32-11.32L96 188.69L218.34 66.34a8 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\\: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\\:copy{--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='M216 32H88a8 8 0 0 0-8 8v40H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-40h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-56 176H48V96h112Zm48-48h-32V88a8 8 0 0 0-8-8H96V48h112Z'/%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-command-palette{z-index:2147483646}.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}.static{position:static}.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%}.left-2{left:.5rem}.left-5px{left:5px}.right--1{right:-.25rem}.right--1px{right:-1px}.right-0{right:0}.right-1\\.5{right:.375rem}.right-2{right:.5rem}.right-4{right:1rem}.top--32px{top:-32px}.top-0\\.5{top:.125rem}.top-1{top:.25rem}.top-1\\.5{top:.375rem}.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-1\\.5{margin-bottom:.375rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.mr-1{margin-right:.25rem}.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}.block{display:block}.hidden{display:none}.size-3\\.5{width:.875rem;height:.875rem}.h-0\\.5{height:.125rem}.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-h-\\[60vh\\]{max-height:60vh}.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-60{width:15rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-96{width:24rem}.w-fit{width:fit-content}.w-full{width:100%}.w-lg{width:32rem}.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-y-2{--vdt-translate-y:-.5rem;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\\/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))}.translate-y-0{--vdt-translate-y:0;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-100{--vdt-scale-x:1;--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))}.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))}.scale-98{--vdt-scale-x:.98;--vdt-scale-y:.98;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}.cursor-not-allowed{cursor:not-allowed}.cursor-grab{cursor:grab}.select-none{user-select:none}.resize{resize:both}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.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}.gap-6{gap:1.5rem}.gap-x-2{column-gap:.5rem}.gap-y-0\\.5{row-gap:.125rem}.space-y-0\\.5>:not([hidden])~:not([hidden]){--vdt-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--vdt-space-y-reverse)));margin-bottom:calc(.125rem * var(--vdt-space-y-reverse))}.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}.last\\:border-b-0:last-child{border-bottom-width:0}.border-amber\\/20{border-color:rgba(251,191,36,.2)}.border-current{border-color:currentColor}.border-primary\\/30{border-color:rgba(213,119,255,.3)}.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-primary\\/40:focus{border-color:rgba(213,119,255,.4)}.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-\\[\\#8881\\]{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.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-black\\/30{background-color:rgba(0,0,0,.3)}.bg-blue{--vdt-bg-opacity:1;background-color:rgba(96,165,250,var(--vdt-bg-opacity))}.bg-blue\\/10{background-color:rgba(96,165,250,.1)}.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-orange\\/10{background-color:rgba(251,146,60,.1)}.bg-primary{--vdt-bg-opacity:1;background-color:rgba(213,119,255,var(--vdt-bg-opacity))}.bg-primary\\/10{background-color:rgba(213,119,255,.1)}.bg-primary\\/15{background-color:rgba(213,119,255,.15)}.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))}.bg-white\\/50{background-color:rgba(255,255,255,.5)}.hover\\:bg-\\[\\#8881\\]:hover{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.hover\\:bg-\\[\\#8882\\]:hover{--vdt-bg-opacity:.13;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-orange\\/20:hover{background-color:rgba(251,146,60,.2)}.hover\\:bg-primary\\/25:hover{background-color:rgba(213,119,255,.25)}.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-1\\.5,.p1\\.5{padding:.375rem}.p-2,.p2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p20{padding:5rem}.p3{padding:.75rem}.p8{padding:2rem}.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}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1,.py1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5,.py1\\.5{padding-top:.375rem;padding-bottom:.375rem}.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}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pt-\\[20vh\\]{padding-top:20vh}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.text-left{text-align:left}.indent{text-indent:1.5rem}.text-\\[10px\\]{font-size:10px}.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-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}.leading-4{line-height:1rem}.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\\:op100{opacity:1}.group:hover .group-hover\\:opacity-100{opacity:1}.group\\/bt:hover .group-hover\\/bt\\:op100{opacity:1}.op25{opacity:.25}.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}.hover\\:op100:hover{opacity:1}.hover\\:op100\\!:hover{opacity:1!important}.hover\\:op60:hover{opacity:.6}.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}.ring-1\\.5{--vdt-ring-width:1.5px;--vdt-ring-offset-shadow:var(--vdt-ring-inset) 0 0 0 var(--vdt-ring-offset-width) var(--vdt-ring-offset-color);--vdt-ring-shadow:var(--vdt-ring-inset) 0 0 0 calc(var(--vdt-ring-width) + var(--vdt-ring-offset-width)) var(--vdt-ring-color);box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.focus\\:ring-1:focus{--vdt-ring-width:1px;--vdt-ring-offset-shadow:var(--vdt-ring-inset) 0 0 0 var(--vdt-ring-offset-width) var(--vdt-ring-offset-color);--vdt-ring-shadow:var(--vdt-ring-inset) 0 0 0 calc(var(--vdt-ring-width) + var(--vdt-ring-offset-width)) var(--vdt-ring-color);box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.ring-purple\\/50{--vdt-ring-color:rgba(192,132,252,.5)}.focus\\:ring-primary\\/30:focus{--vdt-ring-color:rgba(213,119,255,.3)}.backdrop-blur-0{--vdt-backdrop-blur:blur(0);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)}.backdrop-blur-1{--vdt-backdrop-blur:blur(1px);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)}.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-150{transition-duration:.15s}.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\\:bg-black\\/30{background-color:rgba(0,0,0,.3)}.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-right{--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='m181.66 133.66l-80 80a8 8 0 0 1-11.32-11.32L164.69 128L90.34 53.66a8 8 0 0 1 11.32-11.32l80 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-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-dots-six-vertical{--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='M104 60a12 12 0 1 1-12-12a12 12 0 0 1 12 12m60 12a12 12 0 1 0-12-12a12 12 0 0 0 12 12m-72 44a12 12 0 1 0 12 12a12 12 0 0 0-12-12m72 0a12 12 0 1 0 12 12a12 12 0 0 0-12-12m-72 68a12 12 0 1 0 12 12a12 12 0 0 0-12-12m72 0a12 12 0 1 0 12 12a12 12 0 0 0-12-12'/%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-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-keyboard-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 64v128a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8V64a8 8 0 0 1 8-8h192a8 8 0 0 1 8 8' opacity='.2'/%3E%3Cpath d='M224 48H32a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h192a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16m0 144H32V64h192zm-16-64a8 8 0 0 1-8 8H56a8 8 0 0 1 0-16h144a8 8 0 0 1 8 8m0-32a8 8 0 0 1-8 8H56a8 8 0 0 1 0-16h144a8 8 0 0 1 8 8M72 160a8 8 0 0 1-8 8h-8a8 8 0 0 1 0-16h8a8 8 0 0 1 8 8m96 0a8 8 0 0 1-8 8H96a8 8 0 0 1 0-16h64a8 8 0 0 1 8 8m40 0a8 8 0 0 1-8 8h-8a8 8 0 0 1 0-16h8a8 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-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-magnifying-glass-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='M192 112a80 80 0 1 1-80-80a80 80 0 0 1 80 80' opacity='.2'/%3E%3Cpath d='m229.66 218.34l-50.06-50.06a88.21 88.21 0 1 0-11.32 11.31l50.06 50.07a8 8 0 0 0 11.32-11.32M40 112a72 72 0 1 1 72 72a72.08 72.08 0 0 1-72-72'/%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-paint-brush-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 32c0 32.81-31.64 67.43-58.64 91.05A84.4 84.4 0 0 0 133 90.64c23.57-27 58.19-58.64 91-58.64' opacity='.2'/%3E%3Cpath d='M232 32a8 8 0 0 0-8-8c-44.08 0-89.31 49.71-114.43 82.63A60 60 0 0 0 32 164c0 30.88-19.54 44.73-20.47 45.37A8 8 0 0 0 16 224h76a60 60 0 0 0 57.37-77.57C182.3 121.31 232 76.08 232 32M92 208H34.63C41.38 198.41 48 183.92 48 164a44 44 0 1 1 44 44m32.42-94.45q5.14-6.66 10.09-12.55A76.2 76.2 0 0 1 155 121.49q-5.9 4.94-12.55 10.09a60.5 60.5 0 0 0-18.03-18.03m42.7-2.68a92.6 92.6 0 0 0-22-22c31.78-34.53 55.75-45 69.9-47.91c-2.85 14.16-13.37 38.13-47.9 69.91'/%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-spinner-gap-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='M136 32v32a8 8 0 0 1-16 0V32a8 8 0 0 1 16 0m88 88h-32a8 8 0 0 0 0 16h32a8 8 0 0 0 0-16m-45.09 47.6a8 8 0 0 0-11.31 11.31l22.62 22.63a8 8 0 0 0 11.32-11.32ZM128 184a8 8 0 0 0-8 8v32a8 8 0 0 0 16 0v-32a8 8 0 0 0-8-8m-50.91-16.4l-22.63 22.62a8 8 0 0 0 11.32 11.32l22.62-22.63a8 8 0 0 0-11.31-11.31M72 128a8 8 0 0 0-8-8H32a8 8 0 0 0 0 16h32a8 8 0 0 0 8-8m-6.22-73.54a8 8 0 0 0-11.32 11.32L77.09 88.4A8 8 0 0 0 88.4 77.09Z'/%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-wrench-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 96a64 64 0 0 1-94.94 56L73 217a24 24 0 0 1-34-34l65-56.06a64 64 0 0 1 80-90.29L144 80l5.66 26.34L176 112l43.35-40A63.8 63.8 0 0 1 224 96' opacity='.2'/%3E%3Cpath d='M226.76 69a8 8 0 0 0-12.84-2.88l-40.3 37.19l-17.23-3.7l-3.7-17.23l37.19-40.3A8 8 0 0 0 187 29.24A72 72 0 0 0 88 96a72.3 72.3 0 0 0 6 28.94L33.79 177c-.15.12-.29.26-.43.39a32 32 0 0 0 45.26 45.26c.13-.13.27-.28.39-.42L131.06 162A72 72 0 0 0 232 96a71.6 71.6 0 0 0-5.24-27M160 152a56.14 56.14 0 0 1-27.07-7a8 8 0 0 0-9.92 1.77l-55.9 64.74a16 16 0 0 1-22.62-22.62L109.18 133a8 8 0 0 0 1.77-9.93a56 56 0 0 1 58.36-82.31l-31.2 33.81a8 8 0 0 0-1.94 7.1l5.66 26.33a8 8 0 0 0 6.14 6.14l26.35 5.66a8 8 0 0 0 7.1-1.94l33.81-31.2A56.06 56.06 0 0 1 160 152'/%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{--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='m229.66 77.66l-128 128a8 8 0 0 1-11.32 0l-56-56a8 8 0 0 1 11.32-11.32L96 188.69L218.34 66.34a8 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\\: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\\:copy{--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='M216 32H88a8 8 0 0 0-8 8v40H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-40h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-56 176H48V96h112Zm48-48h-32V88a8 8 0 0 0-8-8H96V48h112Z'/%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-command-palette{z-index:2147483646}.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}.static{position:static}.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%}.left-2{left:.5rem}.left-5px{left:5px}.right--1{right:-.25rem}.right--1px{right:-1px}.right-0{right:0}.right-1\\.5{right:.375rem}.right-2{right:.5rem}.right-4{right:1rem}.top--32px{top:-32px}.top-0\\.5{top:.125rem}.top-1{top:.25rem}.top-1\\.5{top:.375rem}.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-1\\.5{margin-bottom:.375rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.mr-1{margin-right:.25rem}.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}.block{display:block}.hidden{display:none}.size-3\\.5{width:.875rem;height:.875rem}.h-0\\.5{height:.125rem}.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-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\\[60vh\\]{max-height:60vh}.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-60{width:15rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-96{width:24rem}.w-fit{width:fit-content}.w-full{width:100%}.w-lg{width:32rem}.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-y-2{--vdt-translate-y:-.5rem;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\\/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))}.translate-y-0{--vdt-translate-y:0;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-100{--vdt-scale-x:1;--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))}.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))}.scale-98{--vdt-scale-x:.98;--vdt-scale-y:.98;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}.cursor-not-allowed{cursor:not-allowed}.cursor-grab{cursor:grab}.select-none{user-select:none}.resize{resize:both}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.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}.gap-6{gap:1.5rem}.gap-x-2{column-gap:.5rem}.gap-y-0\\.5{row-gap:.125rem}.space-y-0\\.5>:not([hidden])~:not([hidden]){--vdt-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--vdt-space-y-reverse)));margin-bottom:calc(.125rem * var(--vdt-space-y-reverse))}.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}.last\\:border-b-0:last-child{border-bottom-width:0}.border-amber\\/20{border-color:rgba(251,191,36,.2)}.border-current{border-color:currentColor}.border-primary\\/30{border-color:rgba(213,119,255,.3)}.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-primary\\/40:focus{border-color:rgba(213,119,255,.4)}.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-\\[\\#8881\\]{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.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-black\\/30{background-color:rgba(0,0,0,.3)}.bg-blue{--vdt-bg-opacity:1;background-color:rgba(96,165,250,var(--vdt-bg-opacity))}.bg-blue\\/10{background-color:rgba(96,165,250,.1)}.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-orange\\/10{background-color:rgba(251,146,60,.1)}.bg-primary{--vdt-bg-opacity:1;background-color:rgba(213,119,255,var(--vdt-bg-opacity))}.bg-primary\\/10{background-color:rgba(213,119,255,.1)}.bg-primary\\/15{background-color:rgba(213,119,255,.15)}.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))}.bg-white\\/50{background-color:rgba(255,255,255,.5)}.hover\\:bg-\\[\\#8881\\]:hover{--vdt-bg-opacity:.07;background-color:rgba(136,136,136,var(--vdt-bg-opacity))}.hover\\:bg-\\[\\#8882\\]:hover{--vdt-bg-opacity:.13;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-orange\\/20:hover{background-color:rgba(251,146,60,.2)}.hover\\:bg-primary\\/25:hover{background-color:rgba(213,119,255,.25)}.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-1\\.5,.p1\\.5{padding:.375rem}.p-2,.p2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p20{padding:5rem}.p3{padding:.75rem}.p8{padding:2rem}.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}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0{padding-top:0;padding-bottom:0}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1,.py1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5,.py1\\.5{padding-top:.375rem;padding-bottom:.375rem}.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}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pt-\\[20vh\\]{padding-top:20vh}.pt-3{padding-top:.75rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.text-left{text-align:left}.indent{text-indent:1.5rem}.text-\\[10px\\]{font-size:10px}.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-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}.leading-4{line-height:1rem}.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\\:op100{opacity:1}.group:hover .group-hover\\:opacity-100{opacity:1}.group\\/bt:hover .group-hover\\/bt\\:op100{opacity:1}.op25{opacity:.25}.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}.hover\\:op100:hover{opacity:1}.hover\\:op100\\!:hover{opacity:1!important}.hover\\:op60:hover{opacity:.6}.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}.ring-1\\.5{--vdt-ring-width:1.5px;--vdt-ring-offset-shadow:var(--vdt-ring-inset) 0 0 0 var(--vdt-ring-offset-width) var(--vdt-ring-offset-color);--vdt-ring-shadow:var(--vdt-ring-inset) 0 0 0 calc(var(--vdt-ring-width) + var(--vdt-ring-offset-width)) var(--vdt-ring-color);box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.focus\\:ring-1:focus{--vdt-ring-width:1px;--vdt-ring-offset-shadow:var(--vdt-ring-inset) 0 0 0 var(--vdt-ring-offset-width) var(--vdt-ring-offset-color);--vdt-ring-shadow:var(--vdt-ring-inset) 0 0 0 calc(var(--vdt-ring-width) + var(--vdt-ring-offset-width)) var(--vdt-ring-color);box-shadow:var(--vdt-ring-offset-shadow), var(--vdt-ring-shadow), var(--vdt-shadow)}.ring-purple\\/50{--vdt-ring-color:rgba(192,132,252,.5)}.focus\\:ring-primary\\/30:focus{--vdt-ring-color:rgba(213,119,255,.3)}.backdrop-blur-0{--vdt-backdrop-blur:blur(0);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)}.backdrop-blur-1{--vdt-backdrop-blur:blur(1px);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)}.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-150{transition-duration:.15s}.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\\:bg-black\\/30{background-color:rgba(0,0,0,.3)}.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/index.ts b/packages/core/src/index.ts index 0aa40d11..2b0db643 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -3,4 +3,7 @@ export { DevTools } from './node/plugins' export type { BuiltinServerFunctions } from './node/rpc' export { createDevToolsMiddleware } from './node/server' export type { DevToolsMiddleware } from './node/server' -export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from 'devframe/node/internal' +export type { + DevframeInternalContext as DevToolsInternalContext, + InternalAnonymousAuthStorage, +} from 'devframe/node/hub-internals' diff --git a/packages/core/src/internal.ts b/packages/core/src/internal.ts index ca0d2ebb..da31e4cc 100644 --- a/packages/core/src/internal.ts +++ b/packages/core/src/internal.ts @@ -1,2 +1,5 @@ -export { getInternalContext } from 'devframe/node/internal' -export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from 'devframe/node/internal' +export { getInternalContext } from 'devframe/node/hub-internals' +export type { + DevframeInternalContext as DevToolsInternalContext, + InternalAnonymousAuthStorage, +} from 'devframe/node/hub-internals' diff --git a/packages/core/src/node/plugins/integration.ts b/packages/core/src/node/plugins/integration.ts index a578091d..456aae6e 100644 --- a/packages/core/src/node/plugins/integration.ts +++ b/packages/core/src/node/plugins/integration.ts @@ -26,7 +26,7 @@ export async function runDevTools(builder: unknown) { const config = (builder as ViteBuilder).config for (const _environment of getDevToolsEnvironments(config)) { try { - const { start } = await import('@vitejs/devtools/cli-commands') + const { start } = await import('../cli-commands') await start(config.devtools.config) } catch (error: any) { diff --git a/packages/core/src/node/rpc/anonymous/auth.ts b/packages/core/src/node/rpc/anonymous/auth.ts index f8586e2e..6a5c582f 100644 --- a/packages/core/src/node/rpc/anonymous/auth.ts +++ b/packages/core/src/node/rpc/anonymous/auth.ts @@ -3,7 +3,7 @@ import process from 'node:process' import * as p from '@clack/prompts' import { defineRpcFunction } from '@vitejs/devtools-kit' import { abortPendingAuth, getTempAuthToken, refreshTempAuthToken, setPendingAuth } from 'devframe/node/auth' -import { getInternalContext } from 'devframe/node/internal' +import { getInternalContext } from 'devframe/node/hub-internals' import { colors as c } from 'devframe/utils/colors' import { MARK_INFO } from '../../constants' @@ -20,7 +20,7 @@ export interface DevToolsAuthReturn { const AUTH_TIMEOUT_MS = 60_000 export const anonymousAuth = defineRpcFunction({ - name: 'vite:anonymous:auth', + name: 'devframe:anonymous:auth', type: 'action', jsonSerializable: true, setup: (context) => { diff --git a/packages/core/src/node/server.ts b/packages/core/src/node/server.ts index b278dd58..1df11b4d 100644 --- a/packages/core/src/node/server.ts +++ b/packages/core/src/node/server.ts @@ -2,7 +2,7 @@ import type { NodeHandler } from 'h3' import type { CreateWsServerOptions } from './ws' import { DEVTOOLS_CONNECTION_META_FILENAME } from '@vitejs/devtools-kit/constants' import { consumeTempAuthToken } from 'devframe/node/auth' -import { getInternalContext } from 'devframe/node/internal' +import { getInternalContext } from 'devframe/node/hub-internals' import { mountStaticHandler } from 'devframe/utils/serve-static' import { defineHandler, getQuery, H3, toNodeHandler } from 'h3' import { dirClientStandalone } from '../dirs' @@ -44,10 +44,10 @@ function generateAuthPageHtml(): string { const data = await r.json() const authToken = data.authToken - localStorage.setItem('__VITE_DEVTOOLS_CONNECTION_AUTH_TOKEN__', authToken) + localStorage.setItem('__DEVFRAME_CONNECTION_AUTH_TOKEN__', authToken) try { - const bc = new BroadcastChannel('vite-devtools-auth') + const bc = new BroadcastChannel('devframe-auth') bc.postMessage({ type: 'auth-update', authToken: authToken }) } catch {} diff --git a/packages/core/src/node/ws.ts b/packages/core/src/node/ws.ts index b8e07c48..ffb0cbe7 100644 --- a/packages/core/src/node/ws.ts +++ b/packages/core/src/node/ws.ts @@ -4,7 +4,7 @@ import type { RpcFunctionsHost } from 'devframe/node' import type { WebSocket } from 'ws' import { AsyncLocalStorage } from 'node:async_hooks' import process from 'node:process' -import { getInternalContext } from 'devframe/node/internal' +import { getInternalContext } from 'devframe/node/hub-internals' import { createRpcServer } from 'devframe/rpc/server' import { attachWsRpcTransport } from 'devframe/rpc/transports/ws-server' import { colors as c } from 'devframe/utils/colors' @@ -26,7 +26,7 @@ export interface CreateWsServerOptions { context: ViteDevToolsNodeContext } -const ANONYMOUS_SCOPE = 'vite:anonymous:' +const ANONYMOUS_SCOPE = 'devframe:anonymous:' function buildWsUrl({ host, port, https }: { host: string, port: number, https: boolean }): string { // 0.0.0.0 / :: / unspecified bindings listen on all interfaces, but a hosted @@ -116,7 +116,7 @@ export async function createWsServer(options: CreateWsServerOptions) { definitions: rpcHost.definitions, onConnected: (ws, req, meta) => { const url = new URL(req.url ?? '', 'http://localhost') - const authToken = url.searchParams.get('vite_devtools_auth_token') ?? undefined + const authToken = url.searchParams.get('devframe_auth_token') ?? undefined const requestOrigin = req.headers.origin if (isClientAuthDisabled) { meta.isTrusted = true diff --git a/packages/kit/package.json b/packages/kit/package.json index fc37518c..9fae2bbd 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -44,6 +44,7 @@ "vite": "*" }, "dependencies": { + "@devframes/hub": "catalog:deps", "birpc": "catalog:deps", "devframe": "catalog:deps", "mlly": "catalog:deps", diff --git a/packages/kit/src/client/client-script.ts b/packages/kit/src/client/client-script.ts index 6f90555f..c284ac52 100644 --- a/packages/kit/src/client/client-script.ts +++ b/packages/kit/src/client/client-script.ts @@ -1,16 +1 @@ -import type { DevToolsMessagesClient } from '../types/messages' -import type { DockEntryState, DocksContext } from './docks' - -/** - * Context for client scripts running in dock entries - */ -export interface DockClientScriptContext extends DocksContext { - /** - * The state of the current dock entry - */ - current: DockEntryState - /** - * Messages client scoped to this dock entry's source - */ - messages: DevToolsMessagesClient -} +export type { DockClientScriptContext } from '@devframes/hub/client' diff --git a/packages/kit/src/client/context.ts b/packages/kit/src/client/context.ts index 2aad9126..c858338e 100644 --- a/packages/kit/src/client/context.ts +++ b/packages/kit/src/client/context.ts @@ -1,5 +1,8 @@ import type { DevToolsClientContext } from './docks' +// The kit owns this global key — the webcomponents host writes to it and +// dock client scripts read from it. Independent of devframe-hub's own +// `CLIENT_CONTEXT_KEY` (which points at a different global). const CLIENT_CONTEXT_KEY = '__VITE_DEVTOOLS_CLIENT_CONTEXT__' /** diff --git a/packages/kit/src/client/docks.ts b/packages/kit/src/client/docks.ts index 9ae43915..27aea0e3 100644 --- a/packages/kit/src/client/docks.ts +++ b/packages/kit/src/client/docks.ts @@ -1,139 +1,14 @@ -import type { DevToolsRpcContext } from 'devframe/client' -import type { EventEmitter } from 'devframe/types' -import type { SharedState } from 'devframe/utils/shared-state' -import type { WhenContext } from 'devframe/utils/when' -import type { DevToolsClientCommand, DevToolsCommandEntry, DevToolsCommandKeybinding } from '../types/commands' -import type { DevToolsDockEntriesGrouped, DevToolsDockEntry, DevToolsDockUserEntry } from '../types/docks' -import type { DevToolsDocksUserSettings } from '../types/settings' - -export type { DevToolsClientRpcHost, RpcClientEvents } from 'devframe/client' - -export interface DockPanelStorage { - mode: 'float' | 'edge' - width: number - height: number - top: number - left: number - position: 'left' | 'right' | 'bottom' | 'top' - open: boolean - inactiveTimeout: number -} - -export type DockClientType = 'embedded' | 'standalone' - -export interface DocksContext extends DevToolsRpcContext { - /** - * Type of the client environment - * - * 'embedded' - running inside an embedded floating panel - * 'standalone' - running inside a standalone window (no user app) - */ - readonly clientType: 'embedded' | 'standalone' - /** - * The panel context - */ - readonly panel: DocksPanelContext - /** - * The docks entries context - */ - readonly docks: DocksEntriesContext - /** - * The commands context for command palette and shortcuts - */ - readonly commands: CommandsContext - /** - * The when-clause context for conditional visibility - */ - readonly when: WhenClauseContext -} - -export interface WhenClauseContext { - /** - * Get the current when-clause context snapshot. - * Returns a reactive object with built-in variables and any custom plugin variables. - */ - readonly context: WhenContext -} - -export type DevToolsClientContext = DocksContext - -export interface DocksPanelContext { - store: DockPanelStorage - isDragging: boolean - isResizing: boolean - readonly isVertical: boolean -} - -export interface DocksEntriesContext { - selectedId: string | null - readonly selected: DevToolsDockEntry | null - entries: DevToolsDockEntry[] - entryToStateMap: Map - groupedEntries: DevToolsDockEntriesGrouped - settings: SharedState - /** - * Get the state of a dock entry by its ID - */ - getStateById: (id: string) => DockEntryState | undefined - /** - * Switch to the selected dock entry, pass `null` to clear the selection - * - * @returns Whether the selection was changed successfully - */ - switchEntry: (id?: string | null) => Promise - /** - * Toggle the selected dock entry - * - * @returns Whether the selection was changed successfully - */ - toggleEntry: (id: string) => Promise -} - -export interface DockEntryState { - entryMeta: DevToolsDockEntry - readonly isActive: boolean - domElements: { - iframe?: HTMLIFrameElement | null - panel?: HTMLDivElement | null - } - events: EventEmitter -} - -export interface DockEntryStateEvents { - 'entry:activated': () => void - 'entry:deactivated': () => void - 'entry:updated': (newMeta: DevToolsDockUserEntry) => void - 'dom:panel:mounted': (panel: HTMLDivElement) => void - 'dom:iframe:mounted': (iframe: HTMLIFrameElement) => void -} - -export interface CommandsContext { - /** - * All commands (server + client) - */ - readonly commands: DevToolsCommandEntry[] - /** - * Palette-visible commands only (filtered by `showInPalette !== false`) - */ - readonly paletteCommands: DevToolsCommandEntry[] - /** - * Register client-side command(s). Returns cleanup function. - */ - register: (cmd: DevToolsClientCommand | DevToolsClientCommand[]) => () => void - /** - * Execute a command by ID. Delegates to RPC for server commands. - */ - execute: (id: string, ...args: any[]) => Promise - /** - * Get effective keybindings for a command (defaults merged with overrides) - */ - getKeybindings: (id: string) => DevToolsCommandKeybinding[] - /** - * User settings store (persisted, includes command shortcuts) - */ - settings: SharedState - /** - * Whether the command palette is open - */ - paletteOpen: boolean -} +export type { + CommandsContext, + DevframeClientContext as DevToolsClientContext, + DevframeClientRpcHost as DevToolsClientRpcHost, + DockClientType, + DockEntryState, + DockEntryStateEvents, + DockPanelStorage, + DocksContext, + DocksEntriesContext, + DocksPanelContext, + RpcClientEvents, + WhenClauseContext, +} from '@devframes/hub/client' diff --git a/packages/kit/src/client/index.ts b/packages/kit/src/client/index.ts index 475395f9..17553e99 100644 --- a/packages/kit/src/client/index.ts +++ b/packages/kit/src/client/index.ts @@ -2,4 +2,17 @@ export * from './client-script' export * from './context' export * from './docks' export * from './remote' -export * from 'devframe/client' + +export { + type DevframeClientRpcHost as DevToolsClientRpcHost, + type DevframeRpcClient as DevToolsRpcClient, + type DevframeRpcClientCall as DevToolsRpcClientCall, + type DevframeRpcClientCallEvent as DevToolsRpcClientCallEvent, + type DevframeRpcClientCallOptional as DevToolsRpcClientCallOptional, + type DevframeRpcClientMode as DevToolsRpcClientMode, + type DevframeRpcClientOptions as DevToolsRpcClientOptions, + type DevframeRpcContext as DevToolsRpcContext, + getDevframeRpcClient as getDevToolsRpcClient, + type RpcStreamingClientHost, + type StreamingSubscribeOptions, +} from '@devframes/hub/client' diff --git a/packages/kit/src/client/remote.ts b/packages/kit/src/client/remote.ts index e38e407c..196b6ed3 100644 --- a/packages/kit/src/client/remote.ts +++ b/packages/kit/src/client/remote.ts @@ -1,122 +1,5 @@ -import type { DevToolsRpcClient, DevToolsRpcClientOptions } from 'devframe/client' -import type { RemoteConnectionInfo } from '../types' -import { getDevToolsRpcClient } from 'devframe/client' -import { REMOTE_CONNECTION_KEY } from 'devframe/constants' - -export type ConnectRemoteDevToolsOptions = Omit - -function base64UrlDecode(value: string): string { - const padLen = (4 - value.length % 4) % 4 - const padded = value.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat(padLen) - const binary = atob(padded) - const bytes = new Uint8Array(binary.length) - for (let i = 0; i < binary.length; i++) - bytes[i] = binary.charCodeAt(i) - return new TextDecoder().decode(bytes) -} - -function extractKeyFromFragment(hash: string): string | null { - if (!hash) - return null - const raw = hash.startsWith('#') ? hash.slice(1) : hash - for (const part of raw.split('&')) { - const [k, v = ''] = part.split('=') - if (k === REMOTE_CONNECTION_KEY) - return decodeURIComponent(v) - } - return null -} - -function extractKeyFromQuery(search: string): string | null { - if (!search) - return null - const params = new URLSearchParams(search.startsWith('?') ? search.slice(1) : search) - return params.get(REMOTE_CONNECTION_KEY) -} - -/** - * Parse a {@link RemoteConnectionInfo} descriptor from the current page's URL - * (or a provided URL/string). Checks the URL fragment first, then the query. - * - * Returns `null` if no descriptor is present. - * Throws if the descriptor is malformed or its schema version is unsupported. - */ -export function parseRemoteConnection(input?: string): RemoteConnectionInfo | null { - let hash = '' - let search = '' - if (input === undefined) { - if (typeof location === 'undefined') - return null - hash = location.hash - search = location.search - } - else { - try { - const parsed = new URL(input, 'http://_') - hash = parsed.hash - search = parsed.search - } - catch { - // Treat as raw fragment or query string. - if (input.startsWith('#')) - hash = input - else if (input.startsWith('?')) - search = input - else - return null - } - } - - const encoded = extractKeyFromFragment(hash) ?? extractKeyFromQuery(search) - if (!encoded) - return null - - let payload: unknown - try { - payload = JSON.parse(base64UrlDecode(encoded)) - } - catch (cause) { - throw new Error('[vite-devtools-kit] Failed to decode remote connection descriptor.', { cause }) - } - - if (!payload || typeof payload !== 'object') - throw new Error('[vite-devtools-kit] Remote connection descriptor must be an object.') - - const info = payload as Partial - if (info.v !== 1) - throw new Error(`[vite-devtools-kit] Unsupported remote connection descriptor version: ${String(info.v)}`) - if (info.backend !== 'websocket' || typeof info.websocket !== 'string' || !info.websocket) - throw new Error('[vite-devtools-kit] Remote connection descriptor must carry a websocket URL.') - if (typeof info.authToken !== 'string' || !info.authToken) - throw new Error('[vite-devtools-kit] Remote connection descriptor must carry an auth token.') - if (typeof info.origin !== 'string') - throw new Error('[vite-devtools-kit] Remote connection descriptor must carry an origin.') - - return info as RemoteConnectionInfo -} - -/** - * One-liner for a hosted DevTools page: reads the connection descriptor from - * the current URL and returns a connected {@link DevToolsRpcClient}. - * - * Pairs with `remote: true` on a `DevToolsViewIframe` registered on the node - * side — the core injects the descriptor into the iframe URL. - * - * @throws if no descriptor is present in the URL. - */ -export async function connectRemoteDevTools( - options: ConnectRemoteDevToolsOptions = {}, -): Promise { - const info = parseRemoteConnection() - if (!info) { - throw new Error( - `[vite-devtools-kit] No remote connection descriptor found in the URL. ` - + `Open this page through a Vite DevTools dock registered with \`remote: true\`.`, - ) - } - return getDevToolsRpcClient({ - ...options, - connectionMeta: info, - authToken: info.authToken, - }) -} +export { + connectRemoteDevframe as connectRemoteDevTools, + type ConnectRemoteDevframeOptions as ConnectRemoteDevToolsOptions, + parseRemoteConnection, +} from '@devframes/hub/client' diff --git a/packages/kit/src/constants.ts b/packages/kit/src/constants.ts index 71f9acc7..a3f94d68 100644 --- a/packages/kit/src/constants.ts +++ b/packages/kit/src/constants.ts @@ -1,7 +1,26 @@ import type { DevToolsDockEntryCategory } from './types/docks' import type { DevToolsDocksUserSettings } from './types/settings' -export * from 'devframe/constants' +// Filename / dirname constants whose *values* are unchanged across the +// devframe `DevTools*` → `Devframe*` rename. Re-export them under both +// names so downstream code that imports `DEVTOOLS_*` keeps compiling. +export { + DEFAULT_STATE_USER_SETTINGS, + DEVFRAME_CONNECTION_META_FILENAME as DEVTOOLS_CONNECTION_META_FILENAME, + DEVFRAME_DOCK_IMPORTS_FILENAME as DEVTOOLS_DOCK_IMPORTS_FILENAME, + DEVFRAME_RPC_DUMP_DIRNAME as DEVTOOLS_RPC_DUMP_DIRNAME, + DEVFRAME_RPC_DUMP_MANIFEST_FILENAME as DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME, + REMOTE_CONNECTION_KEY, +} from '@devframes/hub/constants' + +// Kit-side mount path is pinned at `/__devtools/` regardless of devframe's +// new `/__devframe/` default. The hosted (Vite-mounted) flow always passes +// the base path explicitly to `ctx.views.hostStatic()` and to the Vite +// middleware, so the kit owns the value. +export const DEVTOOLS_MOUNT_PATH = '/__devtools/' +export const DEVTOOLS_MOUNT_PATH_NO_TRAILING_SLASH = '/__devtools' +export const DEVTOOLS_DIRNAME = '__devtools' +export const DEVTOOLS_DOCK_IMPORTS_VIRTUAL_ID = '/__devtools-client-imports.js' export const DEFAULT_CATEGORIES_ORDER: Record = { '~viteplus': -1000, @@ -13,12 +32,4 @@ export const DEFAULT_CATEGORIES_ORDER: Record = { '~builtin': 1000, } satisfies Record -export const DEFAULT_STATE_USER_SETTINGS: () => DevToolsDocksUserSettings = () => ({ - docksHidden: [], - docksCategoriesHidden: [], - docksPinned: [], - docksCustomOrder: {}, - showIframeAddressBar: false, - closeOnOutsideClick: false, - commandShortcuts: {}, -}) +export type { DevToolsDocksUserSettings } diff --git a/packages/kit/src/define.ts b/packages/kit/src/define.ts index 1c5da49b..e7173c5c 100644 --- a/packages/kit/src/define.ts +++ b/packages/kit/src/define.ts @@ -1,24 +1,6 @@ -import type { WhenContext, WhenExpression } from 'devframe/utils/when' -import type { DevToolsDockUserEntry, DevToolsServerCommandInput, JsonRenderSpec, ViteDevToolsNodeContext } from './types' +import type { ViteDevToolsNodeContext } from './types' import { createDefineWrapperWithContext } from 'devframe/rpc' -export const defineRpcFunction = createDefineWrapperWithContext() - -export function defineCommand( - command: Omit & { when?: WhenExpression }, -): DevToolsServerCommandInput { - return command as DevToolsServerCommandInput -} +export { defineCommand, defineDockEntry, defineJsonRenderSpec } from '@devframes/hub' -export function defineDockEntry< - const T extends DevToolsDockUserEntry, - const W extends string = '', ->( - entry: Omit & { when?: WhenExpression }, -): T { - return entry as unknown as T -} - -export function defineJsonRenderSpec(spec: JsonRenderSpec): JsonRenderSpec { - return spec -} +export const defineRpcFunction = createDefineWrapperWithContext() diff --git a/packages/kit/src/node/__tests__/host-docks.test.ts b/packages/kit/src/node/__tests__/host-docks.test.ts deleted file mode 100644 index 1977aea8..00000000 --- a/packages/kit/src/node/__tests__/host-docks.test.ts +++ /dev/null @@ -1,445 +0,0 @@ -import type { DevToolsDockUserEntry, DevToolsViewIframe, RemoteConnectionInfo } from '../../types/docks' -import type { KitNodeContext } from '../context' -import { Buffer } from 'node:buffer' -import { REMOTE_CONNECTION_KEY } from 'devframe/constants' -import { getInternalContext, internalContextMap } from 'devframe/node/internal' -import { describe, expect, it } from 'vitest' -import { DevToolsDockHost } from '../host-docks' - -function createMockContext(): KitNodeContext { - return { - viteConfig: { - server: { host: 'localhost', port: 5173, https: false }, - }, - viteServer: undefined, - mode: 'dev', - host: { - mountStatic: () => {}, - resolveOrigin: () => 'http://localhost:5173', - getStorageDir: () => '/tmp/devframe-test-storage', - }, - } as unknown as KitNodeContext -} - -function decodeDescriptor(url: string): RemoteConnectionInfo { - const match = url.match(new RegExp(`[#&?]${REMOTE_CONNECTION_KEY}=([^&]+)`)) - if (!match) - throw new Error(`No descriptor in URL: ${url}`) - const encoded = match[1]! - const padLen = (4 - encoded.length % 4) % 4 - const padded = encoded.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat(padLen) - return JSON.parse(Buffer.from(padded, 'base64').toString('utf8')) -} - -describe('devToolsDockHost', () => { - const mockContext = { - host: { - mountStatic: () => {}, - resolveOrigin: () => 'http://localhost:5173', - getStorageDir: () => '/tmp/devframe-test-storage', - }, - } as unknown as KitNodeContext - - describe('builtin entries', () => { - it('does not include popup in builtin docks', () => { - const host = new DevToolsDockHost(mockContext) - const builtinEntries = host.values().filter(entry => entry.type === '~builtin') - const builtinIds = builtinEntries.map(entry => entry.id) - - expect(builtinIds).not.toContain('~popup') - }) - }) - - describe('register() collision detection', () => { - it('should register a new dock successfully', () => { - const host = new DevToolsDockHost(mockContext) - const dock: DevToolsDockUserEntry = { - type: 'iframe', - id: 'test-dock', - title: 'Test Dock', - icon: 'test-icon', - url: 'http://localhost:3000', - } - - expect(() => host.register(dock)).not.toThrow() - expect(host.views.has('test-dock')).toBe(true) - }) - - it('should throw error when registering duplicate dock ID', () => { - const host = new DevToolsDockHost(mockContext) - const dock1: DevToolsDockUserEntry = { - type: 'iframe', - id: 'duplicate-dock', - title: 'First Dock', - icon: 'icon1', - url: 'http://localhost:3001', - } - const dock2: DevToolsDockUserEntry = { - type: 'iframe', - id: 'duplicate-dock', - title: 'Second Dock', - icon: 'icon2', - url: 'http://localhost:3002', - } - - host.register(dock1) - - expect(() => host.register(dock2)).toThrow() - expect(() => host.register(dock2)).toThrow('duplicate-dock') - expect(() => host.register(dock2)).toThrow('already registered') - }) - - it('should include the duplicate ID in error message', () => { - const host = new DevToolsDockHost(mockContext) - const dock: DevToolsDockUserEntry = { - type: 'custom-render', - id: 'my-special-panel', - title: 'Special Panel', - icon: 'special', - renderer: { - importFrom: './component.js', - importName: 'MyComponent', - }, - } - - host.register(dock) - - expect(() => host.register(dock)).toThrow('my-special-panel') - }) - - it('should allow different dock IDs', () => { - const host = new DevToolsDockHost(mockContext) - - host.register({ - type: 'iframe', - id: 'dock-1', - title: 'Dock 1', - icon: 'icon1', - url: 'http://localhost:3001', - }) - - host.register({ - type: 'iframe', - id: 'dock-2', - title: 'Dock 2', - icon: 'icon2', - url: 'http://localhost:3002', - }) - - expect(host.views.size).toBe(2) - }) - }) - - describe('update() existence validation', () => { - it('should throw error when updating non-existent dock', () => { - const host = new DevToolsDockHost(mockContext) - const dock: DevToolsDockUserEntry = { - type: 'iframe', - id: 'nonexistent', - title: 'Does Not Exist', - icon: 'icon', - url: 'http://localhost:3000', - } - - expect(() => host.update(dock)).toThrow() - expect(() => host.update(dock)).toThrow('nonexistent') - expect(() => host.update(dock)).toThrow('not registered') - expect(() => host.update(dock)).toThrow('Use register()') - }) - - it('should update existing dock successfully', () => { - const host = new DevToolsDockHost(mockContext) - const dock1: DevToolsDockUserEntry = { - type: 'iframe', - id: 'update-test', - title: 'Original Title', - icon: 'original', - url: 'http://localhost:3001', - } - const dock2: DevToolsDockUserEntry = { - type: 'iframe', - id: 'update-test', - title: 'Updated Title', - icon: 'updated', - url: 'http://localhost:3002', - } - - host.register(dock1) - expect(() => host.update(dock2)).not.toThrow() - - const updated = host.views.get('update-test') - expect(updated?.title).toBe('Updated Title') - if (updated?.type === 'iframe') { - expect(updated.url).toBe('http://localhost:3002') - } - }) - - it('should validate that update only works on existing entries', () => { - const host = new DevToolsDockHost(mockContext) - - // Register one dock - host.register({ - type: 'iframe', - id: 'exists', - title: 'Exists', - icon: 'icon', - url: 'http://localhost:3000', - }) - - // Update should work for existing - expect(() => - host.update({ - type: 'iframe', - id: 'exists', - title: 'Updated', - icon: 'icon', - url: 'http://localhost:3001', - }), - ).not.toThrow() - - // Update should fail for non-existing - expect(() => - host.update({ - type: 'iframe', - id: 'does-not-exist', - title: 'Failed', - icon: 'icon', - url: 'http://localhost:3002', - }), - ).toThrow() - }) - - it('should preserve dock in values() after update', () => { - const host = new DevToolsDockHost(mockContext) - - const { update } = host.register({ - type: 'iframe', - id: 'test', - title: 'Original', - icon: 'icon', - url: 'http://localhost:3000', - }) - - update({ - title: 'Updated', - icon: 'newicon', - url: 'http://localhost:3001', - }) - - const docks = host.values({ includeBuiltin: false }) - expect(docks.length).toBe(1) - expect(docks[0]?.title).toBe('Updated') - }) - }) - - describe('remote iframe docks', () => { - it('allocates a session token and enriches URL with a fragment descriptor', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - const internal = getInternalContext(ctx) - internal.wsEndpoint = { url: 'ws://localhost:7812' } - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/devtools', - remote: true, - }) - - expect(internal.remoteTokens.size).toBe(1) - - const projected = host.values({ includeBuiltin: false })[0] as DevToolsViewIframe - expect(projected.url).toContain('#') - expect(projected.url).toContain(REMOTE_CONNECTION_KEY) - expect(projected.url).not.toContain('?') - - const descriptor = decodeDescriptor(projected.url) - expect(descriptor).toMatchObject({ - v: 1, - backend: 'websocket', - websocket: 'ws://localhost:7812', - origin: 'http://localhost:5173', - }) - expect(descriptor.authToken).toBeTruthy() - - // The token in the descriptor is exactly the one allocated by the registry. - expect(internal.remoteTokens.has(descriptor.authToken)).toBe(true) - internalContextMap.delete(ctx) - }) - - it('emits a query transport when transport=query is requested', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - getInternalContext(ctx).wsEndpoint = { url: 'ws://localhost:7812' } - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/devtools', - remote: { transport: 'query' }, - }) - - const projected = host.values({ includeBuiltin: false })[0] as DevToolsViewIframe - expect(projected.url).toMatch(new RegExp(`\\?${REMOTE_CONNECTION_KEY}=`)) - expect(projected.url).not.toContain('#') - internalContextMap.delete(ctx) - }) - - it('does not inject a default `when` clause for remote docks', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - getInternalContext(ctx).wsEndpoint = { url: 'ws://localhost:7812' } - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/devtools', - remote: true, - }) - - expect(host.views.get('remote-dock')?.when).toBeUndefined() - internalContextMap.delete(ctx) - }) - - it('respects an explicit `when` clause from the author', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - getInternalContext(ctx).wsEndpoint = { url: 'ws://localhost:7812' } - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/devtools', - remote: true, - when: 'clientType == embedded', - }) - - expect(host.views.get('remote-dock')?.when).toBe('clientType == embedded') - internalContextMap.delete(ctx) - }) - - it('revokes the prior token when the dock is force-re-registered', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - const internal = getInternalContext(ctx) - internal.wsEndpoint = { url: 'ws://localhost:7812' } - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/v1', - remote: true, - }) - const firstToken = [...internal.remoteTokens.keys()][0]! - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/v2', - remote: true, - }, true) - - expect(internal.remoteTokens.has(firstToken)).toBe(false) - expect(internal.remoteTokens.size).toBe(1) - internalContextMap.delete(ctx) - }) - - it('leaves non-remote iframe URLs untouched', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - getInternalContext(ctx).wsEndpoint = { url: 'ws://localhost:7812' } - - host.register({ - type: 'iframe', - id: 'plain-dock', - title: 'Plain', - icon: 'ph:app-window-duotone', - url: '/__devtools-plain/', - }) - - const projected = host.values({ includeBuiltin: false })[0] as DevToolsViewIframe - expect(projected.url).toBe('/__devtools-plain/') - internalContextMap.delete(ctx) - }) - - it('returns non-enriched URL before the WS endpoint is known', () => { - const ctx = createMockContext() - const host = new DevToolsDockHost(ctx) - // Note: wsEndpoint intentionally NOT set. - - host.register({ - type: 'iframe', - id: 'remote-dock', - title: 'Remote', - icon: 'ph:globe-duotone', - url: 'https://example.com/devtools', - remote: true, - }) - - const projected = host.values({ includeBuiltin: false })[0] as DevToolsViewIframe - expect(projected.url).toBe('https://example.com/devtools') - internalContextMap.delete(ctx) - }) - }) - - describe('remote token trust', () => { - it('trusts a token when origin matches (originLock on)', () => { - const ctx = createMockContext() - const internal = getInternalContext(ctx) - - const token = internal.allocateRemoteToken('dock-a', 'https://example.com', true) - expect(internal.isRemoteTokenTrusted(token, 'https://example.com')).toBe(true) - expect(internal.isRemoteTokenTrusted(token, 'https://attacker.com')).toBe(false) - expect(internal.isRemoteTokenTrusted(token, undefined)).toBe(false) - internalContextMap.delete(ctx) - }) - - it('trusts a token regardless of origin when originLock is off', () => { - const ctx = createMockContext() - const internal = getInternalContext(ctx) - - const token = internal.allocateRemoteToken('dock-a', 'https://example.com', false) - expect(internal.isRemoteTokenTrusted(token, 'https://example.com')).toBe(true) - expect(internal.isRemoteTokenTrusted(token, 'https://another.com')).toBe(true) - expect(internal.isRemoteTokenTrusted(token, undefined)).toBe(true) - internalContextMap.delete(ctx) - }) - - it('rejects tokens after revocation', () => { - const ctx = createMockContext() - const internal = getInternalContext(ctx) - - const token = internal.allocateRemoteToken('dock-a', 'https://example.com', true) - internal.revokeRemoteToken(token) - expect(internal.isRemoteTokenTrusted(token, 'https://example.com')).toBe(false) - internalContextMap.delete(ctx) - }) - - it('revokeRemoteTokensForDock removes every token tied to the dock', () => { - const ctx = createMockContext() - const internal = getInternalContext(ctx) - - const t1 = internal.allocateRemoteToken('dock-a', 'https://example.com', true) - const t2 = internal.allocateRemoteToken('dock-a', 'https://example.com', true) - const t3 = internal.allocateRemoteToken('dock-b', 'https://example.com', true) - - internal.revokeRemoteTokensForDock('dock-a') - expect(internal.remoteTokens.has(t1)).toBe(false) - expect(internal.remoteTokens.has(t2)).toBe(false) - expect(internal.remoteTokens.has(t3)).toBe(true) - internalContextMap.delete(ctx) - }) - }) -}) diff --git a/packages/kit/src/node/__tests__/host-messages.test.ts b/packages/kit/src/node/__tests__/host-messages.test.ts deleted file mode 100644 index 71521b3b..00000000 --- a/packages/kit/src/node/__tests__/host-messages.test.ts +++ /dev/null @@ -1,325 +0,0 @@ -import type { KitNodeContext } from '../context' -import { describe, expect, it, vi } from 'vitest' -import { DevToolsMessagesHost } from '../host-messages' - -describe('devToolsMessagesHost', () => { - const mockContext = {} as KitNodeContext - - function createHost() { - return new DevToolsMessagesHost(mockContext) - } - - describe('add()', () => { - it('should add a message entry with auto-generated id and timestamp', async () => { - const host = createHost() - const handle = await host.add({ message: 'test', level: 'info' }) - - expect(handle.id).toBeDefined() - expect(handle.entry.message).toBe('test') - expect(handle.entry.level).toBe('info') - expect(handle.entry.timestamp).toBeTypeOf('number') - expect(handle.entry.from).toBe('server') - expect(host.entries.size).toBe(1) - }) - - it('should use provided id and timestamp', async () => { - const host = createHost() - const handle = await host.add({ id: 'my-id', message: 'test', level: 'warn', timestamp: 12345 }) - - expect(handle.id).toBe('my-id') - expect(handle.entry.timestamp).toBe(12345) - }) - - it('should emit message:added event', async () => { - const host = createHost() - const handler = vi.fn() - host.events.on('message:added', handler) - - const handle = await host.add({ message: 'test', level: 'info' }) - - expect(handler).toHaveBeenCalledWith(handle.entry) - }) - - it('should dedup by id — delegates to update() if id exists', async () => { - const host = createHost() - await host.add({ id: 'dup', message: 'first', level: 'info' }) - const handle = await host.add({ id: 'dup', message: 'second', level: 'warn' }) - - expect(host.entries.size).toBe(1) - expect(handle.entry.message).toBe('second') - expect(handle.entry.level).toBe('warn') - // Preserves original id, source, timestamp - expect(handle.id).toBe('dup') - expect(handle.entry.from).toBe('server') - }) - - it('should evict oldest entry when at capacity', async () => { - const host = createHost() - // Add 1000 entries (MAX_ENTRIES) - for (let i = 0; i < 1000; i++) - await host.add({ id: `entry-${i}`, message: `msg ${i}`, level: 'info' }) - - expect(host.entries.size).toBe(1000) - expect(host.entries.has('entry-0')).toBe(true) - - // Adding one more should evict the first - await host.add({ id: 'overflow', message: 'overflow', level: 'info' }) - expect(host.entries.size).toBe(1000) - expect(host.entries.has('entry-0')).toBe(false) - expect(host.entries.has('overflow')).toBe(true) - }) - }) - - describe('update()', () => { - it('should update an existing entry', async () => { - const host = createHost() - await host.add({ id: 'u1', message: 'original', level: 'info' }) - const updated = await host.update('u1', { message: 'changed', level: 'error' }) - - expect(updated).toBeDefined() - expect(updated!.message).toBe('changed') - expect(updated!.level).toBe('error') - // Preserved fields - expect(updated!.id).toBe('u1') - expect(updated!.from).toBe('server') - }) - - it('should return undefined for non-existent id', async () => { - const host = createHost() - expect(await host.update('nope', { message: 'x' })).toBeUndefined() - }) - - it('should emit message:updated event', async () => { - const host = createHost() - await host.add({ id: 'u2', message: 'a', level: 'info' }) - const handler = vi.fn() - host.events.on('message:updated', handler) - - await host.update('u2', { message: 'b' }) - - expect(handler).toHaveBeenCalledOnce() - expect(handler.mock.calls[0]![0].message).toBe('b') - }) - - it('should preserve id, source, and timestamp on update', async () => { - const host = createHost() - const handle = await host.add({ id: 'u3', message: 'orig', level: 'info' }) - const original = handle.entry - const updated = await host.update('u3', { message: 'new' }) - - expect(updated!.id).toBe(original.id) - expect(updated!.from).toBe(original.from) - expect(updated!.timestamp).toBe(original.timestamp) - }) - }) - - describe('remove()', () => { - it('should remove an entry', async () => { - const host = createHost() - await host.add({ id: 'r1', message: 'test', level: 'info' }) - await host.remove('r1') - - expect(host.entries.size).toBe(0) - }) - - it('should emit message:removed event', async () => { - const host = createHost() - await host.add({ id: 'r2', message: 'test', level: 'info' }) - const handler = vi.fn() - host.events.on('message:removed', handler) - - await host.remove('r2') - - expect(handler).toHaveBeenCalledWith('r2') - }) - }) - - describe('clear()', () => { - it('should remove all entries', async () => { - const host = createHost() - await host.add({ message: 'a', level: 'info' }) - await host.add({ message: 'b', level: 'warn' }) - await host.clear() - - expect(host.entries.size).toBe(0) - }) - - it('should emit message:cleared event', async () => { - const host = createHost() - await host.add({ message: 'a', level: 'info' }) - const handler = vi.fn() - host.events.on('message:cleared', handler) - - await host.clear() - - expect(handler).toHaveBeenCalledOnce() - }) - }) - - describe('autoDelete', () => { - it('should auto-delete entry after timeout', async () => { - vi.useFakeTimers() - const host = createHost() - await host.add({ id: 'ad1', message: 'temp', level: 'info', autoDelete: 1000 }) - - expect(host.entries.has('ad1')).toBe(true) - vi.advanceTimersByTime(1000) - expect(host.entries.has('ad1')).toBe(false) - - vi.useRealTimers() - }) - - it('should reset autoDelete timer on update', async () => { - vi.useFakeTimers() - const host = createHost() - await host.add({ id: 'ad2', message: 'temp', level: 'info', autoDelete: 1000 }) - - vi.advanceTimersByTime(500) - await host.update('ad2', { autoDelete: 2000 }) - - vi.advanceTimersByTime(500) - expect(host.entries.has('ad2')).toBe(true) - - vi.advanceTimersByTime(1500) - expect(host.entries.has('ad2')).toBe(false) - - vi.useRealTimers() - }) - - it('should clear autoDelete timer on remove', async () => { - vi.useFakeTimers() - const host = createHost() - await host.add({ id: 'ad3', message: 'temp', level: 'info', autoDelete: 1000 }) - await host.remove('ad3') - - // Should not throw or re-remove after timer fires - vi.advanceTimersByTime(1000) - expect(host.entries.has('ad3')).toBe(false) - - vi.useRealTimers() - }) - }) - - describe('incremental versioning', () => { - it('should track lastModified on add', async () => { - const host = createHost() - await host.add({ id: 'v1', message: 'a', level: 'info' }) - await host.add({ id: 'v2', message: 'b', level: 'info' }) - - const mod1 = host.lastModified.get('v1')! - const mod2 = host.lastModified.get('v2')! - expect(mod1).toBeLessThan(mod2) - }) - - it('should update lastModified on update', async () => { - const host = createHost() - await host.add({ id: 'v3', message: 'a', level: 'info' }) - const modBefore = host.lastModified.get('v3')! - - await host.update('v3', { message: 'b' }) - const modAfter = host.lastModified.get('v3')! - - expect(modAfter).toBeGreaterThan(modBefore) - }) - - it('should remove lastModified on remove', async () => { - const host = createHost() - await host.add({ id: 'v4', message: 'a', level: 'info' }) - await host.remove('v4') - - expect(host.lastModified.has('v4')).toBe(false) - }) - - it('should track removals', async () => { - const host = createHost() - await host.add({ id: 'v5', message: 'a', level: 'info' }) - await host.add({ id: 'v6', message: 'b', level: 'info' }) - await host.remove('v5') - - expect(host.removals).toHaveLength(1) - expect(host.removals[0]!.id).toBe('v5') - expect(host.removals[0]!.time).toBeGreaterThan(0) - }) - - it('should track all removals on clear', async () => { - const host = createHost() - await host.add({ id: 'c1', message: 'a', level: 'info' }) - await host.add({ id: 'c2', message: 'b', level: 'info' }) - await host.clear() - - expect(host.removals).toHaveLength(2) - const ids = host.removals.map(r => r.id) - expect(ids).toContain('c1') - expect(ids).toContain('c2') - }) - - it('should clear lastModified on clear', async () => { - const host = createHost() - await host.add({ id: 'c3', message: 'a', level: 'info' }) - await host.clear() - - expect(host.lastModified.size).toBe(0) - }) - - it('should allow filtering entries by version', async () => { - const host = createHost() - await host.add({ id: 'f1', message: 'a', level: 'info' }) - const versionAfterFirst = (host as any)._clock as number - - await host.add({ id: 'f2', message: 'b', level: 'info' }) - await host.update('f1', { message: 'a updated' }) - - // Entries modified after versionAfterFirst - const modified: string[] = [] - for (const [id] of host.entries) { - const mod = host.lastModified.get(id) - if (mod != null && mod > versionAfterFirst) - modified.push(id) - } - - // Both f2 (added after) and f1 (updated after) should be included - expect(modified).toContain('f1') - expect(modified).toContain('f2') - }) - - it('should allow filtering removals by version', async () => { - const host = createHost() - await host.add({ id: 'r1', message: 'a', level: 'info' }) - await host.add({ id: 'r2', message: 'b', level: 'info' }) - await host.remove('r1') - const versionAfterRemove = (host as any)._clock as number - - await host.add({ id: 'r3', message: 'c', level: 'info' }) - await host.remove('r2') - - // Removals after versionAfterRemove - const removedSince = host.removals - .filter(r => r.time > versionAfterRemove) - .map(r => r.id) - - expect(removedSince).toEqual(['r2']) - }) - }) - - describe('handle', () => { - it('should return a handle with live entry', async () => { - const host = createHost() - const handle = await host.add({ id: 'h1', message: 'initial', level: 'info' }) - - expect(handle.id).toBe('h1') - expect(handle.entry.message).toBe('initial') - - // Update via handle - await handle.update({ message: 'updated' }) - expect(handle.entry.message).toBe('updated') - }) - - it('should dismiss via handle', async () => { - const host = createHost() - const handle = await host.add({ id: 'h2', message: 'temp', level: 'info' }) - - await handle.dismiss() - expect(host.entries.has('h2')).toBe(false) - }) - }) -}) diff --git a/packages/kit/src/node/__tests__/host-terminals.test.ts b/packages/kit/src/node/__tests__/host-terminals.test.ts deleted file mode 100644 index b3203f00..00000000 --- a/packages/kit/src/node/__tests__/host-terminals.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { DevToolsTerminalSession } from '../../types/terminals' -import type { KitNodeContext } from '../context' -import { describe, expect, it, vi } from 'vitest' -import { DevToolsTerminalHost } from '../host-terminals' - -describe('devToolsTerminalHost', () => { - it('disposes bound stream entry on remove', () => { - const host = new DevToolsTerminalHost({} as KitNodeContext) - const session: DevToolsTerminalSession = { - id: 'terminal-1', - title: 'Terminal 1', - status: 'running', - } - - const dispose = vi.fn() - - ;(host as any).sessions.set(session.id, session) - ;(host as any)._boundStreams.set(session.id, { - dispose, - stream: new ReadableStream(), - }) - - host.remove(session) - - expect(dispose).toHaveBeenCalledTimes(1) - expect((host as any)._boundStreams.has(session.id)).toBe(false) - }) -}) diff --git a/packages/kit/src/node/context.ts b/packages/kit/src/node/context.ts index 83c39a44..37d44455 100644 --- a/packages/kit/src/node/context.ts +++ b/packages/kit/src/node/context.ts @@ -1,40 +1,18 @@ -import type { CreateHostContextOptions } from 'devframe/node' -import type { DevToolsNodeContext } from 'devframe/types' +import type { CreateHubContextOptions, DevframeHubContext } from '@devframes/hub/node' import type { ResolvedConfig, ViteDevServer } from 'vite' -import type { DevToolsCommandsHost } from '../types/commands' -import type { DevToolsDockHost } from '../types/docks' -import type { JsonRenderer, JsonRenderSpec } from '../types/json-render' -import type { DevToolsMessagesHost } from '../types/messages' -import type { DevToolsTerminalHost } from '../types/terminals' -import { createHostContext } from 'devframe/node' -import { debounce } from 'perfect-debounce' -import { DevToolsCommandsHost as CommandsHostImpl } from './host-commands' -import { DevToolsDockHost as DocksHostImpl } from './host-docks' -import { DevToolsMessagesHost as MessagesHostImpl } from './host-messages' -import { DevToolsTerminalHost as TerminalsHostImpl } from './host-terminals' +import { createHubContext } from '@devframes/hub/node' /** - * Kit-augmented node context — extends devframe's framework-neutral - * `DevToolsNodeContext` with the hub-level subsystems (`docks`, - * `terminals`, `messages`, `commands`) and the `createJsonRenderer` - * factory, all owned by `@vitejs/devtools-kit`. When kit hosts the - * devtool inside Vite DevTools, also exposes the underlying Vite - * handles. + * Kit-augmented node context — the framework-neutral hub context from + * `@devframes/hub`, plus the Vite-specific slots surfaced when kit hosts + * the devtool inside Vite DevTools. */ -export interface KitNodeContext extends DevToolsNodeContext { - docks: DevToolsDockHost - terminals: DevToolsTerminalHost - messages: DevToolsMessagesHost - commands: DevToolsCommandsHost - /** - * Create a JsonRenderer handle for building json-render powered UIs. - */ - createJsonRenderer: (spec: JsonRenderSpec) => JsonRenderer +export interface KitNodeContext extends DevframeHubContext { readonly viteConfig?: ResolvedConfig readonly viteServer?: ViteDevServer } -export interface CreateKitContextOptions extends CreateHostContextOptions { +export interface CreateKitContextOptions extends CreateHubContextOptions { /** Optional Vite resolved config to surface on the context (for Vite-mounted hubs). */ viteConfig?: ResolvedConfig /** Optional Vite dev server to surface on the context. */ @@ -42,89 +20,19 @@ export interface CreateKitContextOptions extends CreateHostContextOptions { } /** - * Create a kit-level node context: wraps devframe's `createHostContext`, - * attaches the hub hosts (`docks`, `terminals`, `messages`, `commands`), - * and wires the shared-state synchronization that powers the unified - * client UI. + * Create a kit-level node context: wraps `@devframes/hub`'s + * `createHubContext` (which itself wraps devframe's `createHostContext`) + * and attaches the Vite-specific slots. The hub layer owns the + * docks/terminals/messages/commands subsystems and seeds the shared-state + * sync the unified client UI consumes. */ export async function createKitContext(options: CreateKitContextOptions): Promise { - const baseContext = await createHostContext(options) - const context = baseContext as KitNodeContext - - const docks = new DocksHostImpl(context) - const terminals = new TerminalsHostImpl(context) - const messages = new MessagesHostImpl(context) - const commands = new CommandsHostImpl(context) - - context.docks = docks - context.terminals = terminals - context.messages = messages - context.commands = commands + const context = await createHubContext(options) as KitNodeContext if (options.viteConfig) Object.defineProperty(context, 'viteConfig', { value: options.viteConfig, enumerable: true }) if (options.viteServer) Object.defineProperty(context, 'viteServer', { value: options.viteServer, enumerable: true }) - await docks.init() - - let jrCounter = 0 - context.createJsonRenderer = (initialSpec: JsonRenderSpec): JsonRenderer => { - const stateKey = `devframe:json-render:${jrCounter++}` - const statePromise = context.rpc.sharedState.get(stateKey as any, { - initialValue: initialSpec as any, - }) - - return { - _stateKey: stateKey, - async updateSpec(spec) { - const state = await statePromise - state.mutate(() => spec as any) - }, - async updateState(newState) { - const state = await statePromise - state.mutate((draft: any) => { - draft.state = { ...draft.state, ...newState } - }) - }, - } - } - - const debounceMs = options.mode === 'build' ? 0 : 10 - - const docksSharedState = await context.rpc.sharedState.get('devframe:docks', { initialValue: [] }) - const refreshDocks = debounce(() => { - docksSharedState.mutate(() => docks.values()) - }, debounceMs) - docks.events.on('dock:entry:updated', refreshDocks) - - const broadcastTerminals = debounce(() => { - context.rpc.broadcast({ - method: 'devframe:terminals:updated', - args: [], - }) - docksSharedState.mutate(() => docks.values()) - }, debounceMs) - terminals.events.on('terminal:session:updated', broadcastTerminals) - - const broadcastMessages = debounce(() => { - context.rpc.broadcast({ - method: 'devframe:messages:updated', - args: [], - }) - docksSharedState.mutate(() => docks.values()) - }, debounceMs) - messages.events.on('message:added', broadcastMessages) - messages.events.on('message:updated', broadcastMessages) - messages.events.on('message:removed', broadcastMessages) - messages.events.on('message:cleared', broadcastMessages) - - const commandsSharedState = await context.rpc.sharedState.get('devframe:commands', { initialValue: [] }) - const syncCommands = debounce(() => { - commandsSharedState.mutate(() => commands.list()) - }, debounceMs) - commands.events.on('command:registered', syncCommands) - commands.events.on('command:unregistered', syncCommands) - return context } diff --git a/packages/kit/src/node/create-plugin-from-devframe.ts b/packages/kit/src/node/create-plugin-from-devframe.ts index 9e130037..41fba98f 100644 --- a/packages/kit/src/node/create-plugin-from-devframe.ts +++ b/packages/kit/src/node/create-plugin-from-devframe.ts @@ -1,9 +1,8 @@ -import type { DevframeDefinition, DevToolsCapabilities } from 'devframe/types' -import type { DevToolsViewIframe } from '../types/docks' +import type { DevframeCapabilities, DevframeViewIframe } from '@devframes/hub/types' +import type { DevframeDefinition } from 'devframe/types' import type { PluginWithDevTools } from '../types/vite-augment' import type { KitNodeContext } from './context' -import { resolveBasePath } from 'devframe/node/internal' -import { resolve } from 'pathe' +import { mountDevframe } from '@devframes/hub/node' export interface CreatePluginFromDevframeOptions { /** @@ -20,12 +19,12 @@ export interface CreatePluginFromDevframeOptions { * `when`, etc. Cannot change `id`, `type`, or `url` — those are * derived from the devframe definition. */ - dock?: Partial> + dock?: Partial> /** * Capability flags forwarded onto the kit plugin's `devtools` slot. * Defaults to `d.capabilities`. */ - capabilities?: DevToolsCapabilities | { dev?: DevToolsCapabilities | boolean, build?: DevToolsCapabilities | boolean } + capabilities?: DevframeCapabilities | { dev?: DevframeCapabilities | boolean, build?: DevframeCapabilities | boolean } /** * Additional kit-only setup hook. Runs after the devframe-level * `d.setup(ctx)` and after the auto-derived dock entry has been @@ -39,43 +38,25 @@ export interface CreatePluginFromDevframeOptions { /** * Wrap a {@link DevframeDefinition} as a Vite plugin that mounts inside - * `@vitejs/devtools` (Vite DevTools). The kit takes care of mounting - * the SPA at the resolved base path, synthesizing an iframe dock entry - * from the definition's metadata, and threading the kit-augmented - * context into both the devframe-level `d.setup` and the optional - * `options.setup` hook. - * - * For richer kit-specific behavior (registering terminals/commands, - * adding additional dock entries), use `options.setup`. + * `@vitejs/devtools` (Vite DevTools). Delegates the mount work + * (serving the SPA, registering the iframe dock entry, calling + * `d.setup(ctx)`) to `@devframes/hub`'s `mountDevframe`, then runs the + * optional kit-only `options.setup` hook. */ export function createPluginFromDevframe( d: DevframeDefinition, options: CreatePluginFromDevframeOptions = {}, ): PluginWithDevTools { - const base = options.base ?? resolveBasePath(d, 'hosted') - return { name: options.name ?? `devframe:${d.id}`, devtools: { capabilities: options.capabilities ?? (d.capabilities as any), async setup(rawCtx) { const ctx = rawCtx as KitNodeContext - - if (d.cli?.distDir) { - ctx.views.hostStatic(base, resolve(d.cli.distDir)) - } - - ctx.docks.register({ - id: d.id, - title: d.name, - icon: d.icon ?? 'ph:plug-duotone', - ...options.dock, - type: 'iframe', - url: base, - } as DevToolsViewIframe) - - await d.setup(ctx) - + await mountDevframe(ctx, d, { + base: options.base, + dock: options.dock, + }) if (options.setup) { await options.setup(ctx) } diff --git a/packages/kit/src/node/diagnostics.ts b/packages/kit/src/node/diagnostics.ts index 42e3f49f..cb842b54 100644 --- a/packages/kit/src/node/diagnostics.ts +++ b/packages/kit/src/node/diagnostics.ts @@ -1,37 +1,11 @@ import { defineDiagnostics, reporterLog } from 'nostics' -// Kit-side diagnostics for the hub subsystems (docks, terminals, commands, -// messages). The `DTK` prefix is shared with `@vitejs/devtools` (core); -// numbers must not collide. Kit reserves 0050+; core's codes top out -// below that today. +// Kit-side diagnostics. The hub-domain codes (DTK0050-DTK0057) have +// moved upstream into `@devframes/hub` as DF8100-DF8403 since hub now +// owns the docks/terminals/commands hosts. Kit-only codes can be added +// here in the DTK0050+ range as needed. export const diagnostics = defineDiagnostics({ docsBase: 'https://devtools.vite.dev/errors', reporters: [reporterLog], - codes: { - DTK0050: { - why: (p: { id: string }) => `Dock with id "${p.id}" is already registered`, - fix: 'Use the `force` parameter to overwrite an existing registration.', - }, - DTK0051: { - why: 'Cannot change the id of a dock. Use register() to add new docks.', - }, - DTK0052: { - why: (p: { id: string }) => `Dock with id "${p.id}" is not registered. Use register() to add new docks.`, - }, - DTK0053: { - why: (p: { id: string }) => `Terminal session with id "${p.id}" already registered`, - }, - DTK0054: { - why: (p: { id: string }) => `Terminal session with id "${p.id}" not registered`, - }, - DTK0055: { - why: (p: { id: string }) => `Command "${p.id}" is already registered`, - }, - DTK0056: { - why: 'Cannot change the id of a command. Use register() to add new commands.', - }, - DTK0057: { - why: (p: { id: string }) => `Command "${p.id}" is not registered`, - }, - }, + codes: {}, }) diff --git a/packages/kit/src/node/host-commands.ts b/packages/kit/src/node/host-commands.ts deleted file mode 100644 index 6b6d636e..00000000 --- a/packages/kit/src/node/host-commands.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { - DevToolsCommandHandle, - DevToolsCommandsHost as DevToolsCommandsHostType, - DevToolsServerCommandEntry, - DevToolsServerCommandInput, -} from '../types/commands' -import type { KitNodeContext } from './context' -import { createEventEmitter } from 'devframe/utils/events' -import { diagnostics } from './diagnostics' - -export class DevToolsCommandsHost implements DevToolsCommandsHostType { - public readonly commands: DevToolsCommandsHostType['commands'] = new Map() - public readonly events: DevToolsCommandsHostType['events'] = createEventEmitter() - - constructor( - public readonly context: KitNodeContext, - ) {} - - register(command: DevToolsServerCommandInput): DevToolsCommandHandle { - if (this.commands.has(command.id)) { - throw diagnostics.DTK0055({ id: command.id }) - } - this.commands.set(command.id, command) - this.events.emit('command:registered', this.toSerializable(command)) - - return { - id: command.id, - update: (patch: Partial>) => { - if ('id' in patch) { - throw diagnostics.DTK0056() - } - const existing = this.commands.get(command.id) - if (!existing) { - throw diagnostics.DTK0057({ id: command.id }) - } - Object.assign(existing, patch) - this.events.emit('command:registered', this.toSerializable(existing)) - }, - unregister: () => this.unregister(command.id), - } - } - - unregister(id: string): boolean { - const deleted = this.commands.delete(id) - if (deleted) { - this.events.emit('command:unregistered', id) - } - return deleted - } - - async execute(id: string, ...args: any[]): Promise { - const found = this.findCommand(id) - if (!found) { - throw diagnostics.DTK0057({ id }) - } - if (!found.handler) { - throw new Error(`Command "${id}" has no handler (group-only command)`) - } - return found.handler(...args) - } - - list(): DevToolsServerCommandEntry[] { - return Array.from(this.commands.values()).map(cmd => this.toSerializable(cmd)) - } - - private findCommand(id: string): DevToolsServerCommandInput | undefined { - // Check top-level - const topLevel = this.commands.get(id) - if (topLevel) - return topLevel - - // Search children - for (const cmd of this.commands.values()) { - if (cmd.children) { - const child = cmd.children.find((c: DevToolsServerCommandInput) => c.id === id) - if (child) - return child - } - } - - return undefined - } - - private toSerializable(cmd: DevToolsServerCommandInput): DevToolsServerCommandEntry { - const { handler: _, children, ...rest } = cmd - return { - ...rest, - source: 'server', - ...(children - ? { children: children.map((c: DevToolsServerCommandInput) => this.toSerializable(c)) } - : {} - ), - } - } -} diff --git a/packages/kit/src/node/host-docks.ts b/packages/kit/src/node/host-docks.ts deleted file mode 100644 index eb82a05b..00000000 --- a/packages/kit/src/node/host-docks.ts +++ /dev/null @@ -1,209 +0,0 @@ -import type { DevToolsNodeContext } from 'devframe/types' -import type { SharedState } from 'devframe/utils/shared-state' -import type { - DevToolsDockEntry, - DevToolsDockHost as DevToolsDockHostType, - DevToolsDockUserEntry, - DevToolsViewBuiltin, - DevToolsViewIframe, - RemoteConnectionInfo, - RemoteDockOptions, -} from '../types/docks' -import type { DevToolsDocksUserSettings } from '../types/settings' -import type { KitNodeContext } from './context' -import { REMOTE_CONNECTION_KEY } from 'devframe/constants' -import { createStorage } from 'devframe/node' -import { getInternalContext } from 'devframe/node/internal' -import { createEventEmitter } from 'devframe/utils/events' -import { join } from 'pathe' -import { DEFAULT_STATE_USER_SETTINGS } from '../constants' -import { diagnostics } from './diagnostics' - -interface RemoteDockRecord { - token: string - options: Required -} - -function normaliseRemoteOptions(remote: true | RemoteDockOptions): Required { - const opts = remote === true ? {} : remote - return { - transport: opts.transport ?? 'fragment', - originLock: opts.originLock ?? true, - } -} - -function base64UrlEncode(value: string): string { - // URL-safe base64 without padding so the descriptor is compact and safe to - // drop into a URL without escaping. - const bytes = new TextEncoder().encode(value) - let binary = '' - for (const byte of bytes) - binary += String.fromCharCode(byte) - return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') -} - -function buildRemoteUrl(baseUrl: string, payload: RemoteConnectionInfo, transport: 'fragment' | 'query'): string { - const encoded = base64UrlEncode(JSON.stringify(payload)) - const param = `${REMOTE_CONNECTION_KEY}=${encoded}` - if (transport === 'fragment') { - // Replace any existing fragment bearing our key; otherwise append. - const hashIdx = baseUrl.indexOf('#') - if (hashIdx === -1) - return `${baseUrl}#${param}` - const before = baseUrl.slice(0, hashIdx) - return `${before}#${param}` - } - // query - const qIdx = baseUrl.indexOf('?') - const hashIdx = baseUrl.indexOf('#') - const hash = hashIdx === -1 ? '' : baseUrl.slice(hashIdx) - const beforeHash = hashIdx === -1 ? baseUrl : baseUrl.slice(0, hashIdx) - const sep = qIdx === -1 || qIdx >= (hashIdx === -1 ? beforeHash.length : hashIdx) ? '?' : '&' - return `${beforeHash}${sep}${param}${hash}` -} - -export class DevToolsDockHost implements DevToolsDockHostType { - public readonly views: DevToolsDockHostType['views'] = new Map() - public readonly events: DevToolsDockHostType['events'] = createEventEmitter() - public userSettings: SharedState = undefined! - - /** Dock-id → allocated remote token + resolved options. */ - private readonly remoteDocks = new Map() - - constructor( - public readonly context: KitNodeContext, - ) { - - } - - async init() { - this.userSettings = await this.context.rpc.sharedState.get('devframe:user-settings', { - sharedState: createStorage({ - filepath: join(this.context.host.getStorageDir('workspace'), 'settings.json'), - initialValue: DEFAULT_STATE_USER_SETTINGS(), - }), - }) - } - - values({ - includeBuiltin = true, - }: { - includeBuiltin?: boolean - } = {}): DevToolsDockEntry[] { - const context = this.context - const builtinDocksEntries: DevToolsViewBuiltin[] = [ - { - type: '~builtin', - id: '~terminals', - title: 'Terminals', - icon: 'ph:terminal-duotone', - category: '~builtin', - get when() { - return context.terminals.sessions.size === 0 ? 'false' : undefined - }, - }, - { - type: '~builtin', - id: '~messages', - title: 'Messages & Notifications', - icon: 'ph:notification-duotone', - category: '~builtin', - get badge() { - const size = context.messages.entries.size - return size > 0 ? String(size) : undefined - }, - }, - { - type: '~builtin', - id: '~settings', - title: 'Settings', - category: '~builtin', - icon: 'ph:gear-duotone', - }, - ] - - return [ - ...Array.from(this.views.values(), view => this.projectView(view)), - ...(includeBuiltin ? builtinDocksEntries : []), - ] - } - - private projectView(view: DevToolsDockUserEntry): DevToolsDockUserEntry { - if (view.type !== 'iframe' || !view.remote) - return view - const record = this.remoteDocks.get(view.id) - const endpoint = getInternalContext(this.context as DevToolsNodeContext).wsEndpoint - if (!record || !endpoint) - return view - - const payload: RemoteConnectionInfo = { - v: 1, - backend: 'websocket', - websocket: endpoint.url, - authToken: record.token, - origin: this.resolveDevServerOrigin(), - } - return { - ...view, - url: buildRemoteUrl(view.url, payload, record.options.transport), - } satisfies DevToolsViewIframe - } - - private resolveDevServerOrigin(): string { - return this.context.host.resolveOrigin() - } - - register(view: T, force?: boolean): { - update: (patch: Partial) => void - } { - if (this.views.has(view.id) && !force) { - throw diagnostics.DTK0050({ id: view.id }) - } - this.prepareRemoteRegistration(view) - this.views.set(view.id, view) - this.events.emit('dock:entry:updated', view) - - return { - update: (patch) => { - if (patch.id && patch.id !== view.id) { - throw diagnostics.DTK0051() - } - this.update(Object.assign(this.views.get(view.id)!, patch)) - }, - } - } - - update(view: DevToolsDockUserEntry): void { - if (!this.views.has(view.id)) { - throw diagnostics.DTK0052({ id: view.id }) - } - this.prepareRemoteRegistration(view) - this.views.set(view.id, view) - this.events.emit('dock:entry:updated', view) - } - - private prepareRemoteRegistration(view: DevToolsDockUserEntry): void { - const internal = getInternalContext(this.context as DevToolsNodeContext) - // Always revoke any previously allocated token for this dock id — covers - // force re-registration and update() paths. - internal.revokeRemoteTokensForDock(view.id) - this.remoteDocks.delete(view.id) - - if (view.type !== 'iframe' || !view.remote) - return - - const options = normaliseRemoteOptions(view.remote) - let dockOrigin: string - try { - dockOrigin = new URL(view.url).origin - } - catch { - // Relative/invalid URL — origin-lock can't be enforced. Fall back to the - // dev-server origin; this still works because the iframe loads in the - // same browser anyway. - dockOrigin = this.resolveDevServerOrigin() - } - const token = internal.allocateRemoteToken(view.id, dockOrigin, options.originLock) - this.remoteDocks.set(view.id, { token, options }) - } -} diff --git a/packages/kit/src/node/host-messages.ts b/packages/kit/src/node/host-messages.ts deleted file mode 100644 index 79001c41..00000000 --- a/packages/kit/src/node/host-messages.ts +++ /dev/null @@ -1,134 +0,0 @@ -import type { - DevToolsMessageEntry, - DevToolsMessageEntryInput, - DevToolsMessageHandle, - DevToolsMessagesHost as DevToolsMessagesHostType, -} from '../types/messages' -import type { KitNodeContext } from './context' -import { createEventEmitter } from 'devframe/utils/events' -import { nanoid } from 'devframe/utils/nanoid' - -const MAX_ENTRIES = 1000 - -export class DevToolsMessagesHost implements DevToolsMessagesHostType { - public readonly entries: DevToolsMessagesHostType['entries'] = new Map() - public readonly events: DevToolsMessagesHostType['events'] = createEventEmitter() - - /** Tracks when each entry was last added or updated (monotonic) */ - readonly lastModified = new Map() - /** Tracks recently removed entry IDs with their removal time */ - readonly removals: Array<{ id: string, time: number }> = [] - - private _autoDeleteTimers = new Map>() - private _clock = 0 - - private _tick(): number { - return ++this._clock - } - - constructor( - public readonly context: KitNodeContext, - ) {} - - async add(input: DevToolsMessageEntryInput): Promise { - // Dedupe: if an entry with the same explicit id exists, update it instead - if (input.id && this.entries.has(input.id)) { - await this.update(input.id, input) - return this._createHandle(input.id) - } - - const entry: DevToolsMessageEntry = { - ...input, - id: input.id ?? nanoid(), - timestamp: input.timestamp ?? Date.now(), - from: (input as any).from ?? 'server', - } - - // FIFO eviction when at capacity - if (this.entries.size >= MAX_ENTRIES) { - const oldest = this.entries.keys().next().value! - await this.remove(oldest) - } - - this.entries.set(entry.id, entry) - this.lastModified.set(entry.id, this._tick()) - this.events.emit('message:added', entry) - - if (entry.autoDelete) { - this._autoDeleteTimers.set(entry.id, setTimeout(() => { - this.remove(entry.id) - }, entry.autoDelete)) - } - - return this._createHandle(entry.id) - } - - async update(id: string, patch: Partial): Promise { - const existing = this.entries.get(id) - if (!existing) - return undefined - - const updated: DevToolsMessageEntry = { - ...existing, - ...patch, - id: existing.id, - from: existing.from, - timestamp: existing.timestamp, - } - - this.entries.set(id, updated) - this.lastModified.set(id, this._tick()) - this.events.emit('message:updated', updated) - - // Reset autoDelete timer if changed - if (patch.autoDelete !== undefined) { - const timer = this._autoDeleteTimers.get(id) - if (timer) { - clearTimeout(timer) - this._autoDeleteTimers.delete(id) - } - if (patch.autoDelete) { - this._autoDeleteTimers.set(id, setTimeout(() => { - this.remove(id) - }, patch.autoDelete)) - } - } - - return updated - } - - async remove(id: string): Promise { - const timer = this._autoDeleteTimers.get(id) - if (timer) { - clearTimeout(timer) - this._autoDeleteTimers.delete(id) - } - this.entries.delete(id) - this.lastModified.delete(id) - this.removals.push({ id, time: this._tick() }) - this.events.emit('message:removed', id) - } - - async clear(): Promise { - for (const timer of this._autoDeleteTimers.values()) - clearTimeout(timer) - this._autoDeleteTimers.clear() - const tick = this._tick() - for (const id of this.entries.keys()) - this.removals.push({ id, time: tick }) - this.entries.clear() - this.lastModified.clear() - this.events.emit('message:cleared') - } - - private _createHandle(id: string): DevToolsMessageHandle { - // eslint-disable-next-line ts/no-this-alias - const host = this - return { - get entry() { return host.entries.get(id)! }, - get id() { return id }, - update: patch => host.update(id, patch), - dismiss: () => host.remove(id), - } - } -} diff --git a/packages/kit/src/node/host-terminals.ts b/packages/kit/src/node/host-terminals.ts deleted file mode 100644 index 1c29c8dc..00000000 --- a/packages/kit/src/node/host-terminals.ts +++ /dev/null @@ -1,201 +0,0 @@ -import type { RpcStreamingChannel } from 'devframe/types' -import type { Result as TinyExecResult } from 'tinyexec' -import type { - DevToolsChildProcessExecuteOptions, - DevToolsChildProcessTerminalSession, - DevToolsTerminalHost as DevToolsTerminalHostType, - DevToolsTerminalSession, - DevToolsTerminalSessionBase, -} from '../types/terminals' -import type { KitNodeContext } from './context' -import process from 'node:process' -import { createEventEmitter } from 'devframe/utils/events' -import { diagnostics } from './diagnostics' - -type PartialWithoutId = Partial & { id: string } - -/** - * Channel name used for terminal stream output. Stable, well-known so the - * standalone client (`packages/core/src/client/webcomponents/state/terminals.ts`) - * can subscribe by name. - */ -const TERMINAL_STREAM_CHANNEL = 'devframe:terminals' as const -const TERMINAL_REPLAY_WINDOW = 1000 - -export class DevToolsTerminalHost implements DevToolsTerminalHostType { - public readonly sessions: DevToolsTerminalHostType['sessions'] = new Map() - public readonly events: DevToolsTerminalHostType['events'] = createEventEmitter() - - private _boundStreams = new Map void - stream: ReadableStream - }>() - - private _channel?: RpcStreamingChannel - - constructor( - public readonly context: KitNodeContext, - ) { - } - - /** - * Lazily acquire the streaming channel — `context.rpc` isn't assigned - * until after every host is constructed, so we can't grab it in the - * constructor. - */ - private getStreamingChannel(): RpcStreamingChannel | undefined { - if (this._channel) - return this._channel - if (!this.context.rpc?.streaming) - return undefined - this._channel = this.context.rpc.streaming.create( - TERMINAL_STREAM_CHANNEL, - { replayWindow: TERMINAL_REPLAY_WINDOW }, - ) - return this._channel - } - - register(session: DevToolsTerminalSession): DevToolsTerminalSession { - if (this.sessions.has(session.id)) { - throw diagnostics.DTK0053({ id: session.id }) - } - this.sessions.set(session.id, session) - this.bindStream(session) - this.events.emit('terminal:session:updated', session) - return session - } - - update(patch: PartialWithoutId): void { - if (!this.sessions.has(patch.id)) { - throw diagnostics.DTK0054({ id: patch.id }) - } - const session = this.sessions.get(patch.id)! - Object.assign(session, patch) - this.sessions.set(patch.id, session) - this.bindStream(session) - this.events.emit('terminal:session:updated', session) - } - - remove(session: DevToolsTerminalSession): void { - this._boundStreams.get(session.id)?.dispose() - this.sessions.delete(session.id) - this.events.emit('terminal:session:updated', session) - this._boundStreams.delete(session.id) - } - - private bindStream(session: DevToolsTerminalSession) { - // Skip when the same stream is already bound - if (this._boundStreams.has(session.id) && this._boundStreams.get(session.id)?.stream === session.stream) - return - - // Dispose the previous stream - this._boundStreams.get(session.id)?.dispose() - this._boundStreams.delete(session.id) - - // If new stream is not available, skip - if (!session.stream) - return - - session.buffer ||= [] - const sessionBuffer = session.buffer - - const channel = this.getStreamingChannel() - // The streaming channel reuses `session.id` as the stream id so clients - // can subscribe immediately after seeing the session in - // `devframe:terminals:list`. - const sink = channel?.start({ id: session.id }) - - const writer = new WritableStream({ - write(chunk) { - // Mirror to the legacy session.buffer used by `terminals:read` — - // unbounded history kept for the snapshot endpoint. - sessionBuffer.push(chunk) - sink?.write(chunk) - }, - close() { - sink?.close() - }, - abort(reason) { - sink?.error(reason) - }, - }) - session.stream.pipeTo(writer).catch(() => { - // pipeTo rejection surfaces via writer.abort -> sink.error already. - }) - this._boundStreams.set(session.id, { - dispose: () => { - if (sink && !sink.closed) - sink.close() - }, - stream: session.stream, - }) - } - - async startChildProcess( - executeOptions: DevToolsChildProcessExecuteOptions, - terminal: Omit, - ): Promise { - if (this.sessions.has(terminal.id)) { - throw diagnostics.DTK0053({ id: terminal.id }) - } - const { exec } = await import('tinyexec') - - let controller: ReadableStreamDefaultController | undefined - const stream = new ReadableStream({ - start(_controller) { - controller = _controller - }, - }) - - function createChildProcess() { - const cp = exec( - executeOptions.command, - executeOptions.args || [], - { - nodeOptions: { - env: { - COLORS: 'true', - FORCE_COLOR: 'true', - ...(executeOptions.env || {}), - }, - cwd: executeOptions.cwd ?? process.cwd(), - stdio: 'pipe', - }, - }, - ) - - ;(async () => { - for await (const chunk of cp) { - controller?.enqueue(chunk) - } - })() - - return cp - } - - let cp: TinyExecResult | undefined = createChildProcess() - - const restart = async () => { - cp?.kill() - cp = createChildProcess() - } - const terminate = async () => { - cp?.kill() - cp = undefined - } - - const session: DevToolsChildProcessTerminalSession = { - ...terminal, - status: 'running', - stream, - type: 'child-process', - executeOptions, - getChildProcess: () => cp?.process, - terminate, - restart, - } - this.register(session) - - return Promise.resolve(session) - } -} diff --git a/packages/kit/src/node/index.ts b/packages/kit/src/node/index.ts index af160279..f4cd38f0 100644 --- a/packages/kit/src/node/index.ts +++ b/packages/kit/src/node/index.ts @@ -1,8 +1,16 @@ export * from './context' export * from './create-plugin-from-devframe' -export * from './host-commands' -export * from './host-docks' -export * from './host-messages' -export * from './host-terminals' export * from './utils' export * from './vite-host' + +// The hub-side host implementations and the `mountDevframe` primitive +// power kit's `createKitContext` / `createPluginFromDevframe` under the +// hood. Re-export the class names under the legacy `DevTools*` aliases +// so downstream code that already imported them keeps compiling. +export { + DevframeCommandsHost as DevToolsCommandsHost, + DevframeDocksHost as DevToolsDockHost, + DevframeMessagesHost as DevToolsMessagesHost, + DevframeTerminalsHost as DevToolsTerminalHost, + mountDevframe, +} from '@devframes/hub/node' diff --git a/packages/kit/src/node/vite-host.ts b/packages/kit/src/node/vite-host.ts index ffb67814..23889ac7 100644 --- a/packages/kit/src/node/vite-host.ts +++ b/packages/kit/src/node/vite-host.ts @@ -1,4 +1,4 @@ -import type { DevToolsHost } from 'devframe/types' +import type { DevframeHost } from 'devframe/types' import type { ResolvedConfig, ViteDevServer } from 'vite' import { homedir } from 'node:os' import { join } from 'node:path' @@ -15,7 +15,7 @@ export interface CreateViteDevToolsHostOptions { workspaceRoot?: string } -export function createViteDevToolsHost(options: CreateViteDevToolsHostOptions): DevToolsHost { +export function createViteDevToolsHost(options: CreateViteDevToolsHostOptions): DevframeHost { const { viteConfig, viteServer } = options const workspaceRoot = options.workspaceRoot ?? viteConfig.root diff --git a/packages/kit/src/types/commands.ts b/packages/kit/src/types/commands.ts index 96c48113..63721798 100644 --- a/packages/kit/src/types/commands.ts +++ b/packages/kit/src/types/commands.ts @@ -1,142 +1,12 @@ -import type { EventEmitter } from 'devframe/types' - -// --- Keybinding --- - -export interface DevToolsCommandKeybinding { - /** - * Keyboard shortcut string. - * Use "Mod" for platform-aware modifier (Cmd on macOS, Ctrl elsewhere). - * Examples: "Mod+K", "Mod+Shift+P", "Alt+N" - */ - key: string -} - -// --- Command Entry --- - -export interface DevToolsCommandBase { - /** - * Unique namespaced ID, e.g. "vite:open-in-editor" - */ - id: string - title: string - description?: string - /** - * Iconify icon string, e.g. "ph:pencil-duotone" - */ - icon?: string - category?: string - /** - * Whether to show in command palette. Default: true - * - * - `true` — show the command and flatten its children into search results - * - `false` — hide the command entirely from the palette - * - `'without-children'` — show the command but don't flatten children into top-level search (children are still accessible via drill-down) - */ - showInPalette?: boolean | 'without-children' - /** - * Optional context expression for conditional visibility. - * When set, the command is only shown in the palette and only executable - * when the expression evaluates to true. - * - * Uses the same syntax as keybinding `when` clauses. - * @example 'clientType == embedded' - * @example 'dockOpen && !paletteOpen' - */ - when?: string - /** - * Default keyboard shortcut(s) for this command - */ - keybindings?: DevToolsCommandKeybinding[] -} - -/** - * Server command input — what plugins pass to `ctx.commands.register()`. - */ -export interface DevToolsServerCommandInput extends DevToolsCommandBase { - /** - * Handler for this command. Optional if the command only serves as a group for children. - */ - handler?: (...args: any[]) => any | Promise - /** - * Static sub-commands. Two levels max (parent → children). - * Each child must have a globally unique `id`. - */ - children?: DevToolsServerCommandInput[] -} - -/** - * Serializable server command entry — sent over RPC (no handler). - */ -export interface DevToolsServerCommandEntry extends DevToolsCommandBase { - source: 'server' - children?: DevToolsServerCommandEntry[] -} - -/** - * Client command — registered in the webcomponent context. - */ -export interface DevToolsClientCommand extends DevToolsCommandBase { - source: 'client' - /** - * Action for this command. Optional if the command only serves as a group for children. - * Return sub-commands for dynamic nested palette menus (runtime submenus). - */ - action?: (...args: any[]) => void | DevToolsClientCommand[] | Promise - /** - * Static sub-commands. Two levels max (parent → children). - */ - children?: DevToolsClientCommand[] -} - -/** - * Union of command entries visible in the palette. - */ -export type DevToolsCommandEntry = DevToolsServerCommandEntry | DevToolsClientCommand - -// --- Server Host --- - -export interface DevToolsCommandHandle { - readonly id: string - update: (patch: Partial>) => void - unregister: () => void -} - -export interface DevToolsCommandsHostEvents { - 'command:registered': (command: DevToolsServerCommandEntry) => void - 'command:unregistered': (id: string) => void -} - -export interface DevToolsCommandsHost { - readonly commands: Map - readonly events: EventEmitter - - /** - * Register a command (with optional children). - */ - register: (command: DevToolsServerCommandInput) => DevToolsCommandHandle - - /** - * Unregister a command by ID (removes parent and all children). - */ - unregister: (id: string) => boolean - - /** - * Execute a command by ID. Searches top-level and children. - * Throws if not found or if command has no handler. - */ - execute: (id: string, ...args: any[]) => Promise - - /** - * Returns serializable list (no handlers), preserving tree structure. - */ - list: () => DevToolsServerCommandEntry[] -} - -// --- Shortcut Overrides (shared state) --- - -export interface DevToolsCommandShortcutOverrides { - /** - * Command ID → keybinding overrides. Empty array = shortcut disabled. - */ - [commandId: string]: DevToolsCommandKeybinding[] -} +export type { + DevframeClientCommand as DevToolsClientCommand, + DevframeCommandBase as DevToolsCommandBase, + DevframeCommandEntry as DevToolsCommandEntry, + DevframeCommandHandle as DevToolsCommandHandle, + DevframeCommandKeybinding as DevToolsCommandKeybinding, + DevframeCommandShortcutOverrides as DevToolsCommandShortcutOverrides, + DevframeCommandsHost as DevToolsCommandsHost, + DevframeCommandsHostEvents as DevToolsCommandsHostEvents, + DevframeServerCommandEntry as DevToolsServerCommandEntry, + DevframeServerCommandInput as DevToolsServerCommandInput, +} from '@devframes/hub/types' diff --git a/packages/kit/src/types/docks.ts b/packages/kit/src/types/docks.ts index ae10370c..9a97d792 100644 --- a/packages/kit/src/types/docks.ts +++ b/packages/kit/src/types/docks.ts @@ -1,171 +1,27 @@ -import type { ConnectionMeta, EventEmitter } from 'devframe/types' -import type { JsonRenderer } from './json-render' - -export interface DevToolsDockHost { - readonly views: Map - readonly events: EventEmitter<{ - 'dock:entry:updated': (entry: DevToolsDockUserEntry) => void - }> - - register: (entry: T, force?: boolean) => { - update: (patch: Partial) => void - } - update: (entry: DevToolsDockUserEntry) => void - values: (options?: { includeBuiltin?: boolean }) => DevToolsDockEntry[] -} - -// TODO: refine categories more clearly -export type DevToolsDockEntryCategory = 'app' | 'framework' | 'web' | 'advanced' | 'default' | '~viteplus' | '~builtin' - -export type DevToolsDockEntryIcon = string | { light: string, dark: string } - -export interface DevToolsDockEntryBase { - id: string - title: string - icon: DevToolsDockEntryIcon - /** - * The default order of the entry in the dock. - * The higher the number the earlier it appears. - * @default 0 - */ - defaultOrder?: number - /** - * The category of the entry - * @default 'default' - */ - category?: DevToolsDockEntryCategory - /** - * Conditional visibility expression. - * When set, the dock entry is only visible when the expression evaluates to true. - * Uses the same syntax as command `when` clauses. - * - * Set to `'false'` to unconditionally hide the entry. - * - * @example 'clientType == embedded' - * @see {@link import('devframe/utils/when').evaluateWhen} - */ - when?: string - /** - * Badge text to display on the dock icon (e.g., unread count) - */ - badge?: string -} - -export interface ClientScriptEntry { - /** - * The filepath or module name to import from - */ - importFrom: string - /** - * The name to import the module as - * - * @default 'default' - */ - importName?: string -} - -export interface DevToolsViewIframe extends DevToolsDockEntryBase { - type: 'iframe' - url: string - /** - * The id of the iframe, if multiple tabs is assigned with the same id, the iframe will be shared. - * - * When not provided, it would be treated as a unique frame. - */ - frameId?: string - /** - * Optional client script to import into the iframe - */ - clientScript?: ClientScriptEntry - /** - * Enable remote-UI mode: the core injects a connection descriptor - * (WS URL + pre-approved auth token) into the iframe URL so a hosted - * page can connect back via `connectRemoteDevTools()` from - * `@vitejs/devtools-kit/client` — without needing to ship a dist with - * the plugin. - * - * Requires dev mode (no effect in build mode — no WS server exists). - * When enabled, the dock is automatically hidden in build mode unless - * the author provides an explicit `when` clause. - * - * @example - * remote: true - * remote: { transport: 'query', originLock: false } - */ - remote?: boolean | RemoteDockOptions -} - -export interface RemoteDockOptions { - /** - * How to pass the connection descriptor to the hosted page. - * - * - `'fragment'` (default): appended as `#vite-devtools-kit-connection=...`. - * Not sent in HTTP requests or Referer headers — safest for auth tokens. - * - `'query'`: appended as `?vite-devtools-kit-connection=...`. Use when - * your hosting platform rewrites fragments or your SPA router repurposes - * the fragment for navigation. The token will appear in server access - * logs and outbound Referer headers. - * - * @default 'fragment' - */ - transport?: 'fragment' | 'query' - /** - * Reject WS handshakes whose `Origin` header doesn't match the dock URL - * origin. Turn off when the same hosted app is served from multiple - * origins (e.g. preview deploys). - * - * @default true - */ - originLock?: boolean -} - -export interface RemoteConnectionInfo extends ConnectionMeta { - backend: 'websocket' - websocket: string - v: 1 - authToken: string - origin: string -} - -export type DevToolsViewLauncherStatus = 'idle' | 'loading' | 'success' | 'error' - -export interface DevToolsViewLauncher extends DevToolsDockEntryBase { - type: 'launcher' - launcher: { - icon?: DevToolsDockEntryIcon - title: string - status?: DevToolsViewLauncherStatus - error?: string - description?: string - buttonStart?: string - buttonLoading?: string - onLaunch: () => Promise - } -} - -export interface DevToolsViewAction extends DevToolsDockEntryBase { - type: 'action' - action: ClientScriptEntry -} - -export interface DevToolsViewCustomRender extends DevToolsDockEntryBase { - type: 'custom-render' - renderer: ClientScriptEntry -} - -export interface DevToolsViewBuiltin extends DevToolsDockEntryBase { - type: '~builtin' - id: '~terminals' | '~messages' | '~client-auth-notice' | '~settings' | '~popup' -} - -export interface DevToolsViewJsonRender extends DevToolsDockEntryBase { - type: 'json-render' - /** JsonRenderer handle created by ctx.createJsonRenderer() */ - ui: JsonRenderer -} - -export type DevToolsDockUserEntry = DevToolsViewIframe | DevToolsViewAction | DevToolsViewCustomRender | DevToolsViewLauncher | DevToolsViewJsonRender - -export type DevToolsDockEntry = DevToolsDockUserEntry | DevToolsViewBuiltin - -export type DevToolsDockEntriesGrouped = [category: string, entries: DevToolsDockEntry[]][] +import type { DevframeDockEntryCategory } from '@devframes/hub/types' + +export type { + ClientScriptEntry, + DevframeDockEntriesGrouped as DevToolsDockEntriesGrouped, + DevframeDockEntry as DevToolsDockEntry, + DevframeDockEntryBase as DevToolsDockEntryBase, + DevframeDockEntryIcon as DevToolsDockEntryIcon, + DevframeDocksHost as DevToolsDockHost, + DevframeDockUserEntry as DevToolsDockUserEntry, + DevframeViewAction as DevToolsViewAction, + DevframeViewBuiltin as DevToolsViewBuiltin, + DevframeViewCustomRender as DevToolsViewCustomRender, + DevframeViewIframe as DevToolsViewIframe, + DevframeViewJsonRender as DevToolsViewJsonRender, + DevframeViewLauncher as DevToolsViewLauncher, + DevframeViewLauncherStatus as DevToolsViewLauncherStatus, + RemoteConnectionInfo, + RemoteDockOptions, +} from '@devframes/hub/types' + +/** + * The kit's dock-entry category union extends hub's framework-neutral set + * with the Vite-specific `~viteplus` slot used by Vite DevTools to group + * Vite Plus integrations above the default categories. + */ +export type DevToolsDockEntryCategory = DevframeDockEntryCategory | '~viteplus' diff --git a/packages/kit/src/types/index.ts b/packages/kit/src/types/index.ts index 32d32dc2..5d4df09d 100644 --- a/packages/kit/src/types/index.ts +++ b/packages/kit/src/types/index.ts @@ -11,31 +11,26 @@ export * from './terminals' export * from './vite-augment' export * from './vite-plugin' -export type { RpcDefinitionsFilter, RpcDefinitionsToFunctions } from 'devframe/rpc' - -// NOTE: we re-export devframe's types individually rather than using -// `export * from 'devframe/types'` because the rolldown-plugin-dts step -// fails with a `MemberExpression` AST error on namespace re-exports -// from external packages (tsdown 0.21 / rolldown-plugin-dts 0.23). -// Revisit once upstream supports it. export type { ConnectionMeta, - DevToolsCapabilities, - DevToolsDiagnosticsDefinition, - DevToolsDiagnosticsHost, - DevToolsDiagnosticsLogger, - DevToolsHost, - DevToolsNodeRpcSession, - DevToolsRpcClientFunctions, - DevToolsRpcServerFunctions, - DevToolsRpcSharedStates, - DevToolsViewHost, + DevframeCapabilities as DevToolsCapabilities, + DevframeDiagnosticsDefinition as DevToolsDiagnosticsDefinition, + DevframeDiagnosticsHost as DevToolsDiagnosticsHost, + DevframeDiagnosticsLogger as DevToolsDiagnosticsLogger, + DevframeHost as DevToolsHost, + DevframeNodeRpcSession as DevToolsNodeRpcSession, + DevframeRpcClientFunctions as DevToolsRpcClientFunctions, + DevframeRpcServerFunctions as DevToolsRpcServerFunctions, + DevframeRpcSharedStates as DevToolsRpcSharedStates, + DevframeViewHost as DevToolsViewHost, EntriesToObject, EventEmitter, EventsMap, EventUnsubscribe, PartialWithoutId, RpcBroadcastOptions, + RpcDefinitionsFilter, + RpcDefinitionsToFunctions, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost, @@ -43,4 +38,10 @@ export type { RpcStreamingChannelOptions, RpcStreamingHost, Thenable, -} from 'devframe/types' +} from '@devframes/hub/types' + +// `DevframeNodeContext` is the base framework-neutral context — hub does +// not re-export it because hub itself ships `DevframeHubContext` as the +// canonical hub-augmented surface. The kit aliases it for back-compat +// with code that referenced `DevToolsNodeContext` directly. +export type { DevframeNodeContext as DevToolsNodeContext } from 'devframe/types' diff --git a/packages/kit/src/types/json-render.ts b/packages/kit/src/types/json-render.ts index 336af7cc..55005631 100644 --- a/packages/kit/src/types/json-render.ts +++ b/packages/kit/src/types/json-render.ts @@ -1,29 +1,5 @@ -export interface JsonRenderElement { - type: string - props?: Record - children?: string[] - /** json-render event bindings (e.g. `{ press: { action: "my:action" } }`) */ - on?: Record - /** json-render visibility condition */ - visible?: unknown - /** json-render repeat binding */ - repeat?: unknown - /** Allow additional json-render element fields */ - [key: string]: unknown -} - -export interface JsonRenderSpec { - root: string - elements: Record - /** Initial client-side state model for $state/$bindState expressions */ - state?: Record -} - -export interface JsonRenderer { - /** Replace the entire spec */ - updateSpec: (spec: JsonRenderSpec) => void | Promise - /** Update json-render state values (shallow merge into spec.state) */ - updateState: (state: Record) => void | Promise - /** Internal: shared state key used by the client to subscribe */ - readonly _stateKey: string -} +export type { + JsonRenderElement, + JsonRenderer, + JsonRenderSpec, +} from '@devframes/hub/types' diff --git a/packages/kit/src/types/messages.ts b/packages/kit/src/types/messages.ts index fa273b3e..55a0e09d 100644 --- a/packages/kit/src/types/messages.ts +++ b/packages/kit/src/types/messages.ts @@ -1,146 +1,11 @@ -import type { EventEmitter } from 'devframe/types' - -export type DevToolsMessageLevel = 'info' | 'warn' | 'error' | 'success' | 'debug' -export type DevToolsMessageEntryFrom = 'server' | 'browser' - -export interface DevToolsMessageElementPosition { - /** CSS selector for the element */ - selector?: string - /** Bounding box of the element */ - boundingBox?: { x: number, y: number, width: number, height: number } - /** Human-readable description of the element */ - description?: string -} - -export interface DevToolsMessageFilePosition { - /** Absolute or relative file path */ - file: string - /** Line number (1-based) */ - line?: number - /** Column number (1-based) */ - column?: number -} - -export interface DevToolsMessageEntry { - /** - * Unique identifier for this message entry (auto-generated if not provided) - */ - id: string - /** - * Short title or summary of the message - */ - message: string - /** - * Optional detailed description or explanation - */ - description?: string - /** - * Severity level, determines color and icon - */ - level: DevToolsMessageLevel - /** - * Optional stack trace string - */ - stacktrace?: string - /** - * Optional DOM element position info (e.g., for a11y issues) - */ - elementPosition?: DevToolsMessageElementPosition - /** - * Optional source file position info (e.g., for lint errors) - */ - filePosition?: DevToolsMessageFilePosition - /** - * Whether this message should also appear as a toast notification - */ - notify?: boolean - /** - * Origin of the message entry, automatically set by the context - */ - from: DevToolsMessageEntryFrom - /** - * Grouping category (e.g., 'a11y', 'lint', 'runtime', 'test') - */ - category?: string - /** - * Optional tags/labels for filtering - */ - labels?: string[] - /** - * Time in ms to auto-dismiss the toast notification (client-side) - */ - autoDismiss?: number - /** - * Time in ms to auto-delete this message entry (server-side) - */ - autoDelete?: number - /** - * Timestamp when the message was created (auto-generated if not provided) - */ - timestamp: number - /** - * Status of the message entry (e.g., 'loading' while an operation is in progress). - * Defaults to 'idle' when not specified. - */ - status?: 'loading' | 'idle' -} - -/** - * Input type for creating a message entry. - * `id`, `timestamp`, and `from` are auto-filled by the host. - */ -export type DevToolsMessageEntryInput = Omit & { - id?: string - timestamp?: number -} - -export interface DevToolsMessageHandle { - /** The underlying message entry data */ - readonly entry: DevToolsMessageEntry - /** Shortcut to entry.id */ - readonly id: string - /** Partial update of this message entry */ - update: (patch: Partial) => Promise - /** Remove this message entry */ - dismiss: () => Promise -} - -export interface DevToolsMessagesClient { - /** - * Add a message entry. Returns a Promise resolving to a handle for subsequent updates/dismissal. - * Can be used without `await` for fire-and-forget usage. - */ - add: (input: DevToolsMessageEntryInput) => Promise - /** Remove a message entry by id */ - remove: (id: string) => Promise - /** Clear all message entries */ - clear: () => Promise -} - -export interface DevToolsMessagesHost { - readonly entries: Map - readonly events: EventEmitter<{ - 'message:added': (entry: DevToolsMessageEntry) => void - 'message:updated': (entry: DevToolsMessageEntry) => void - 'message:removed': (id: string) => void - 'message:cleared': () => void - }> - - /** - * Add a new message entry. If an entry with the same `id` already exists, it will be updated instead. - * Returns a handle for subsequent updates/dismissal. Can be used without `await` for fire-and-forget. - */ - add: (entry: DevToolsMessageEntryInput) => Promise - /** - * Update an existing message entry by id (partial update) - */ - update: (id: string, patch: Partial) => Promise - /** - * Remove a message entry by id - */ - remove: (id: string) => Promise - /** - * Clear all message entries - */ - clear: () => Promise -} +export type { + DevframeMessageElementPosition as DevToolsMessageElementPosition, + DevframeMessageEntry as DevToolsMessageEntry, + DevframeMessageEntryFrom as DevToolsMessageEntryFrom, + DevframeMessageEntryInput as DevToolsMessageEntryInput, + DevframeMessageFilePosition as DevToolsMessageFilePosition, + DevframeMessageHandle as DevToolsMessageHandle, + DevframeMessageLevel as DevToolsMessageLevel, + DevframeMessagesClient as DevToolsMessagesClient, + DevframeMessagesHost as DevToolsMessagesHost, +} from '@devframes/hub/types' diff --git a/packages/kit/src/types/settings.ts b/packages/kit/src/types/settings.ts index e79f1363..165992f9 100644 --- a/packages/kit/src/types/settings.ts +++ b/packages/kit/src/types/settings.ts @@ -1,11 +1 @@ -import type { DevToolsCommandShortcutOverrides } from './commands' - -export interface DevToolsDocksUserSettings { - docksHidden: string[] - docksCategoriesHidden: string[] - docksPinned: string[] - docksCustomOrder: Record - showIframeAddressBar: boolean - closeOnOutsideClick: boolean - commandShortcuts: DevToolsCommandShortcutOverrides -} +export type { DevframeDocksUserSettings as DevToolsDocksUserSettings } from '@devframes/hub/types' diff --git a/packages/kit/src/types/terminals.ts b/packages/kit/src/types/terminals.ts index 396c036c..45561179 100644 --- a/packages/kit/src/types/terminals.ts +++ b/packages/kit/src/types/terminals.ts @@ -1,48 +1,8 @@ -import type { EventEmitter } from 'devframe/types' -import type { ChildProcess } from 'node:child_process' -import type { DevToolsDockEntryIcon } from './docks' - -export interface DevToolsTerminalHost { - readonly sessions: Map - readonly events: EventEmitter<{ - 'terminal:session:updated': (session: DevToolsTerminalSession) => void - }> - - register: (session: DevToolsTerminalSession) => DevToolsTerminalSession - update: (session: DevToolsTerminalSession) => void - - startChildProcess: ( - executeOptions: DevToolsChildProcessExecuteOptions, - terminal: Omit, - ) => Promise -} - -export type DevToolsTerminalStatus = 'running' | 'stopped' | 'error' - -export interface DevToolsTerminalSessionBase { - id: string - title: string - description?: string - status: DevToolsTerminalStatus - icon?: DevToolsDockEntryIcon -} - -export interface DevToolsTerminalSession extends DevToolsTerminalSessionBase { - buffer?: string[] - stream?: ReadableStream -} - -export interface DevToolsChildProcessExecuteOptions { - command: string - args: string[] - cwd?: string - env?: Record -} - -export interface DevToolsChildProcessTerminalSession extends DevToolsTerminalSession { - type: 'child-process' - executeOptions: DevToolsChildProcessExecuteOptions - getChildProcess: () => ChildProcess | undefined - terminate: () => Promise - restart: () => Promise -} +export type { + DevframeChildProcessExecuteOptions as DevToolsChildProcessExecuteOptions, + DevframeChildProcessTerminalSession as DevToolsChildProcessTerminalSession, + DevframeTerminalsHost as DevToolsTerminalHost, + DevframeTerminalSession as DevToolsTerminalSession, + DevframeTerminalSessionBase as DevToolsTerminalSessionBase, + DevframeTerminalStatus as DevToolsTerminalStatus, +} from '@devframes/hub/types' diff --git a/packages/kit/src/types/vite-plugin.ts b/packages/kit/src/types/vite-plugin.ts index 7ebdcb0c..d715a5cc 100644 --- a/packages/kit/src/types/vite-plugin.ts +++ b/packages/kit/src/types/vite-plugin.ts @@ -1,11 +1,11 @@ -import type { DevToolsCapabilities } from 'devframe/types' +import type { DevframeCapabilities } from '@devframes/hub/types' import type { ResolvedConfig, ViteDevServer } from 'vite' import type { KitNodeContext } from '../node/context' export interface DevToolsPluginOptions { capabilities?: { - dev?: DevToolsCapabilities | boolean - build?: DevToolsCapabilities | boolean + dev?: DevframeCapabilities | boolean + build?: DevframeCapabilities | boolean } setup: (context: ViteDevToolsNodeContext) => void | Promise } @@ -16,7 +16,7 @@ export interface DevToolsPluginOptions { * Vite-specific slots (`viteConfig`, `viteServer`). Plugins running * under `@vitejs/devtools` rely on this surface; portable devframe * apps should target {@link KitNodeContext} or the framework-neutral - * `DevToolsNodeContext` from `devframe/types`. + * `DevframeNodeContext` from `devframe/types`. */ export interface ViteDevToolsNodeContext extends KitNodeContext { readonly viteConfig: ResolvedConfig diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6e28aed..42eb972f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,6 +37,9 @@ catalogs: specifier: ^7.2.0 version: 7.2.0 deps: + '@devframes/hub': + specifier: ^0.5.2 + version: 0.5.2 '@pnpm/read-project-manifest': specifier: ^1001.2.6 version: 1001.2.6 @@ -47,8 +50,8 @@ catalogs: specifier: ^4.0.0 version: 4.0.0 devframe: - specifier: ^0.4.1 - version: 0.4.1 + specifier: ^0.5.2 + version: 0.5.2 diff: specifier: ^9.0.0 version: 9.0.0 @@ -89,8 +92,8 @@ catalogs: specifier: ^2.0.0 version: 2.0.0 tinyexec: - specifier: ^1.1.2 - version: 1.1.2 + specifier: ^1.2.2 + version: 1.2.2 tinyglobby: specifier: ^0.2.16 version: 0.2.16 @@ -519,7 +522,7 @@ importers: version: 1.1.1 tinyexec: specifier: catalog:deps - version: 1.1.2 + version: 1.2.2 tsdown: specifier: catalog:build version: 0.22.0(@vitejs/devtools@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.3)(ioredis@5.10.1)(typescript@6.0.3)(vite@8.0.14))(publint@0.3.21)(tsx@4.22.3)(typescript@6.0.3)(unrun@0.2.37(synckit@0.11.12))(vue-tsc@3.3.1(typescript@6.0.3)) @@ -576,7 +579,7 @@ importers: version: 4.8.4(change-case@5.4.4)(focus-trap@8.0.0)(fuse.js@7.3.0)(idb-keyval@6.2.3)(typescript@6.0.3)(vite@8.0.14(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0))(vitepress@2.0.0-alpha.17(@types/node@25.0.3)(@vitejs/devtools@packages+core)(change-case@5.4.4)(esbuild@0.28.0)(fuse.js@7.3.0)(idb-keyval@6.2.3)(jiti@2.7.0)(oxc-minify@0.131.0)(postcss@8.5.15)(terser@5.44.1)(tsx@4.22.3)(typescript@6.0.3)(yaml@2.9.0))(vue@3.5.34(typescript@6.0.3)) devframe: specifier: catalog:deps - version: 0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + version: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) mermaid: specifier: catalog:docs version: 11.15.0 @@ -609,7 +612,7 @@ importers: version: 1.60.0 tinyexec: specifier: catalog:deps - version: 1.1.2 + version: 1.2.2 vite: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) @@ -695,7 +698,7 @@ importers: version: link:../../packages/kit tinyexec: specifier: catalog:deps - version: 1.1.2 + version: 1.2.2 devDependencies: serve: specifier: catalog:devtools @@ -709,6 +712,9 @@ importers: packages/core: dependencies: + '@devframes/hub': + specifier: catalog:deps + version: 0.5.2(devframe@0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3)) '@vitejs/devtools-kit': specifier: workspace:* version: link:../kit @@ -723,7 +729,7 @@ importers: version: 7.0.0 devframe: specifier: catalog:deps - version: 0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + version: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) h3: specifier: catalog:deps version: 2.0.1-rc.22 @@ -744,7 +750,7 @@ importers: version: 2.1.0 tinyexec: specifier: catalog:deps - version: 1.1.2 + version: 1.2.2 vue: specifier: catalog:deps version: 3.5.34(typescript@6.0.3) @@ -794,12 +800,15 @@ importers: packages/kit: dependencies: + '@devframes/hub': + specifier: catalog:deps + version: 0.5.2(devframe@0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3)) birpc: specifier: catalog:deps version: 4.0.0 devframe: specifier: catalog:deps - version: 0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + version: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) mlly: specifier: catalog:deps version: 1.8.2 @@ -814,7 +823,7 @@ importers: version: 2.1.0 tinyexec: specifier: catalog:deps - version: 1.1.2 + version: 1.2.2 devDependencies: human-id: specifier: catalog:inlined @@ -854,7 +863,7 @@ importers: version: 3.2.0 devframe: specifier: catalog:deps - version: 0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + version: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) diff: specifier: catalog:deps version: 9.0.0 @@ -972,7 +981,7 @@ importers: version: 4.0.0 devframe: specifier: catalog:deps - version: 0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + version: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) pathe: specifier: catalog:deps version: 2.0.3 @@ -1033,7 +1042,7 @@ importers: version: 4.0.0 devframe: specifier: catalog:deps - version: 0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + version: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) envinfo: specifier: catalog:deps version: 7.21.0 @@ -1354,6 +1363,11 @@ packages: '@colordx/core@5.4.3': resolution: {integrity: sha512-kIxYSfA5T8HXjav55UaaH/o/cKivF6jCCGIb8eqtcsfI46wsvlSiT8jMDyrl779qLec3c2c2oHBZo4oAhvbjrQ==} + '@devframes/hub@0.5.2': + resolution: {integrity: sha512-qMkBFw1OqhPuNs1tQWkRq0z0Tg49kXNu53bs59tdF4lytKupatWVnL3cpsVPqn+Q5P7A70r99BKTcm+prMtHqw==} + peerDependencies: + devframe: 0.5.2 + '@docsearch/css@4.5.4': resolution: {integrity: sha512-gzO4DJwyM9c4YEPHwaLV1nUCDC2N6yoh0QJj44dce2rcfN71mB+jpu3+F+Y/KMDF1EKV0C3m54leSWsraE94xg==} @@ -5190,6 +5204,14 @@ packages: '@modelcontextprotocol/sdk': optional: true + devframe@0.5.2: + resolution: {integrity: sha512-8dIdlOmuY+6NcCsaI2qS0uRLTZ3SvpejY8OYVbXvdWSQV7pvjdWaYNZhVfOfCSd/a5dSCgSge4vW4DCyJSf7+g==} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.0.0 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -7970,8 +7992,8 @@ packages: resolution: {integrity: sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==} engines: {node: ^16.14.0 || >= 17.3.0} - tinyexec@1.1.2: - resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + tinyexec@1.2.2: + resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} engines: {node: '>=18'} tinyglobby@0.2.16: @@ -8350,6 +8372,14 @@ packages: typescript: optional: true + valibot@1.4.1: + resolution: {integrity: sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -8735,6 +8765,18 @@ packages: utf-8-validate: optional: true + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + wsl-utils@0.1.0: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} @@ -8874,13 +8916,13 @@ snapshots: '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.6.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 '@antfu/ni@30.1.0': dependencies: fzf: 0.5.2 package-manager-detector: 1.6.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 '@antfu/utils@9.3.0': {} @@ -9111,6 +9153,15 @@ snapshots: '@colordx/core@5.4.3': {} + '@devframes/hub@0.5.2(devframe@0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3))': + dependencies: + birpc: 4.0.0 + devframe: 0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3) + nostics: 0.2.0 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + tinyexec: 1.2.2 + '@docsearch/css@4.5.4': {} '@docsearch/js@4.5.4': {} @@ -9637,7 +9688,7 @@ snapshots: srvx: 0.11.15 std-env: 4.1.0 tinyclip: 0.1.12 - tinyexec: 1.1.2 + tinyexec: 1.2.2 ufo: 1.6.4 youch: 4.1.1 optionalDependencies: @@ -12018,6 +12069,10 @@ snapshots: dependencies: valibot: 1.4.0(typescript@6.0.3) + '@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@6.0.3))': + dependencies: + valibot: 1.4.1(typescript@6.0.3) + '@vercel/nft@1.5.0(rollup@4.60.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 @@ -12052,7 +12107,7 @@ snapshots: mlly: 1.8.2 pathe: 2.0.3 perfect-debounce: 2.1.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 vite: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.3)(ioredis@5.10.1)(typescript@6.0.3)(vite@8.0.14))(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -12069,7 +12124,7 @@ snapshots: nostics: 0.2.0 pathe: 2.0.3 perfect-debounce: 2.1.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 vite: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@0.2.0)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -12103,7 +12158,7 @@ snapshots: unconfig: 7.5.0 unstorage: 1.17.5(db0@0.3.4)(idb-keyval@6.2.3)(ioredis@5.10.1) vue-virtual-scroller: 3.0.4(vue@3.5.34(typescript@6.0.3)) - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12157,7 +12212,7 @@ snapshots: unconfig: 7.5.0 unstorage: 1.17.5(db0@0.3.4)(idb-keyval@6.2.3)(ioredis@5.10.1) vue-virtual-scroller: 3.0.4(vue@3.5.34(typescript@6.0.3)) - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12201,10 +12256,10 @@ snapshots: obug: 2.1.1 pathe: 2.0.3 perfect-debounce: 2.1.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 vite: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.3)(ioredis@5.10.1)(typescript@6.0.3)(vite@8.0.14))(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) vue: 3.5.34(typescript@6.0.3) - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12246,10 +12301,10 @@ snapshots: obug: 2.1.1 pathe: 2.0.3 perfect-debounce: 2.1.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 vite: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@0.2.0)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) vue: 3.5.34(typescript@6.0.3) - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -13010,7 +13065,7 @@ snapshots: jsonc-parser: 3.3.1 package-manager-detector: 1.6.0 semver: 7.8.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 unconfig: 7.5.0 yaml: 2.9.0 @@ -13577,15 +13632,35 @@ snapshots: devframe@0.4.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3): dependencies: - '@valibot/to-json-schema': 1.7.0(valibot@1.4.0(typescript@6.0.3)) + '@valibot/to-json-schema': 1.7.0(valibot@1.4.1(typescript@6.0.3)) birpc: 4.0.0 cac: 7.0.0 h3: 2.0.1-rc.22 mrmime: 2.0.1 nostics: 0.2.0 pathe: 2.0.3 - valibot: 1.4.0(typescript@6.0.3) - ws: 8.20.1 + valibot: 1.4.1(typescript@6.0.3) + ws: 8.21.0 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6) + transitivePeerDependencies: + - bufferutil + - crossws + - typescript + - utf-8-validate + optional: true + + devframe@0.5.2(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(typescript@6.0.3): + dependencies: + '@valibot/to-json-schema': 1.7.0(valibot@1.4.1(typescript@6.0.3)) + birpc: 4.0.0 + cac: 7.0.0 + h3: 2.0.1-rc.22 + mrmime: 2.0.1 + nostics: 0.2.0 + pathe: 2.0.3 + valibot: 1.4.1(typescript@6.0.3) + ws: 8.21.0 optionalDependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6) transitivePeerDependencies: @@ -15981,7 +16056,7 @@ snapshots: dependencies: citty: 0.2.2 pathe: 2.0.3 - tinyexec: 1.1.2 + tinyexec: 1.2.2 object-assign@4.1.1: optional: true @@ -17244,7 +17319,7 @@ snapshots: tinyclip@0.1.12: {} - tinyexec@1.1.2: {} + tinyexec@1.2.2: {} tinyglobby@0.2.16: dependencies: @@ -17301,7 +17376,7 @@ snapshots: rolldown: 1.0.2 rolldown-plugin-dts: 0.25.0(rolldown@1.0.2)(typescript@6.0.3)(vue-tsc@3.2.9(typescript@6.0.3)) semver: 7.8.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tree-kill: 1.2.2 unconfig-core: 7.5.0 @@ -17330,7 +17405,7 @@ snapshots: rolldown: 1.0.2 rolldown-plugin-dts: 0.25.0(rolldown@1.0.2)(typescript@6.0.3)(vue-tsc@3.3.1(typescript@6.0.3)) semver: 7.8.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tree-kill: 1.2.2 unconfig-core: 7.5.0 @@ -17359,7 +17434,7 @@ snapshots: rolldown: 1.0.2 rolldown-plugin-dts: 0.25.0(rolldown@1.0.2)(typescript@6.0.3)(vue-tsc@3.2.9(typescript@6.0.3)) semver: 7.8.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tree-kill: 1.2.2 unconfig-core: 7.5.0 @@ -17388,7 +17463,7 @@ snapshots: rolldown: 1.0.2 rolldown-plugin-dts: 0.25.0(rolldown@1.0.2)(typescript@6.0.3)(vue-tsc@3.2.9(typescript@6.0.3)) semver: 7.8.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tree-kill: 1.2.2 unconfig-core: 7.5.0 @@ -17771,6 +17846,10 @@ snapshots: optionalDependencies: typescript: 6.0.3 + valibot@1.4.1(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + vary@1.1.2: {} vfile-message@4.0.3: @@ -18142,7 +18221,7 @@ snapshots: picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 vite: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@packages+core)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) @@ -18170,7 +18249,7 @@ snapshots: picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.1.2 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 vite: 8.0.14(@types/node@25.0.3)(@vitejs/devtools@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))(@pnpm/logger@1001.0.1)(db0@0.3.4)(idb-keyval@6.2.3)(ioredis@5.10.1)(typescript@6.0.3)(vite@8.0.14))(esbuild@0.28.0)(jiti@2.7.0)(terser@5.44.1)(tsx@4.22.3)(yaml@2.9.0) @@ -18374,6 +18453,8 @@ snapshots: ws@8.20.1: {} + ws@8.21.0: {} + wsl-utils@0.1.0: dependencies: is-wsl: 3.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index bdeeade1..4beaf407 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,6 +11,7 @@ strictPeerDependencies: false trustPolicy: no-downgrade trustPolicyExclude: - langium + - tinyexec packages: - packages/* - examples/* @@ -52,12 +53,13 @@ catalogs: unplugin-vue: ^7.2.0 vite: ^8.0.14 deps: + '@devframes/hub': ^0.5.2 '@pnpm/read-project-manifest': ^1001.2.6 '@rolldown/debug': ^1.0.2 actionspack: ^0.1.4 birpc: ^4.0.0 cac: ^7.0.0 - devframe: ^0.4.1 + devframe: ^0.5.2 diff: ^9.0.0 envinfo: ^7.21.0 get-port-please: ^3.2.0 @@ -71,7 +73,7 @@ catalogs: perfect-debounce: ^2.1.0 publint: ^0.3.21 structured-clone-es: ^2.0.0 - tinyexec: ^1.1.2 + tinyexec: ^1.2.2 tinyglobby: ^0.2.16 unconfig: ^7.5.0 unstorage: ^1.17.5 diff --git a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.d.ts b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.d.ts index 234088b2..4e4ee785 100644 --- a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.d.ts +++ b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.d.ts @@ -1,96 +1,39 @@ /** * Generated by tsnapi — public API snapshot of `@vitejs/devtools-kit/client` */ -// #region Interfaces -export interface CommandsContext { - readonly commands: DevToolsCommandEntry[]; - readonly paletteCommands: DevToolsCommandEntry[]; - register: (_: DevToolsClientCommand | DevToolsClientCommand[]) => () => void; - execute: (_: string, ..._: any[]) => Promise; - getKeybindings: (_: string) => DevToolsCommandKeybinding[]; - settings: SharedState; - paletteOpen: boolean; -} -export interface DockClientScriptContext extends DocksContext { - current: DockEntryState; - messages: DevToolsMessagesClient; -} -export interface DockEntryState { - entryMeta: DevToolsDockEntry; - readonly isActive: boolean; - domElements: { - iframe?: HTMLIFrameElement | null; - panel?: HTMLDivElement | null; - }; - events: EventEmitter; -} -export interface DockEntryStateEvents { - 'entry:activated': () => void; - 'entry:deactivated': () => void; - 'entry:updated': (_: DevToolsDockUserEntry) => void; - 'dom:panel:mounted': (_: HTMLDivElement) => void; - 'dom:iframe:mounted': (_: HTMLIFrameElement) => void; -} -export interface DockPanelStorage { - mode: 'float' | 'edge'; - width: number; - height: number; - top: number; - left: number; - position: 'left' | 'right' | 'bottom' | 'top'; - open: boolean; - inactiveTimeout: number; -} -export interface DocksContext extends DevToolsRpcContext { - readonly clientType: 'embedded' | 'standalone'; - readonly panel: DocksPanelContext; - readonly docks: DocksEntriesContext; - readonly commands: CommandsContext; - readonly when: WhenClauseContext; -} -export interface DocksEntriesContext { - selectedId: string | null; - readonly selected: DevToolsDockEntry | null; - entries: DevToolsDockEntry[]; - entryToStateMap: Map; - groupedEntries: DevToolsDockEntriesGrouped; - settings: SharedState; - getStateById: (_: string) => DockEntryState | undefined; - switchEntry: (_?: string | null) => Promise; - toggleEntry: (_: string) => Promise; -} -export interface DocksPanelContext { - store: DockPanelStorage; - isDragging: boolean; - isResizing: boolean; - readonly isVertical: boolean; -} -export interface WhenClauseContext { - readonly context: WhenContext; -} -// #endregion - -// #region Types -export type ConnectRemoteDevToolsOptions = Omit; -export type DevToolsClientContext = DocksContext; -export type DockClientType = 'embedded' | 'standalone'; -// #endregion - // #region Functions -export declare function connectRemoteDevTools(_?: ConnectRemoteDevToolsOptions): Promise; export declare function getDevToolsClientContext(): DevToolsClientContext | undefined; -export declare function parseRemoteConnection(_?: string): RemoteConnectionInfo | null; // #endregion // #region Variables export declare const CLIENT_CONTEXT_KEY: string; // #endregion -// #region Re-exports -export * from "devframe/client"; -// #endregion - // #region Other +export { CommandsContext } +export { connectRemoteDevTools } +export { ConnectRemoteDevToolsOptions } +export { DevToolsClientContext } export { DevToolsClientRpcHost } +export { DevToolsRpcClient } +export { DevToolsRpcClientCall } +export { DevToolsRpcClientCallEvent } +export { DevToolsRpcClientCallOptional } +export { DevToolsRpcClientMode } +export { DevToolsRpcClientOptions } +export { DevToolsRpcContext } +export { DockClientScriptContext } +export { DockClientType } +export { DockEntryState } +export { DockEntryStateEvents } +export { DockPanelStorage } +export { DocksContext } +export { DocksEntriesContext } +export { DocksPanelContext } +export { getDevToolsRpcClient } +export { parseRemoteConnection } export { RpcClientEvents } +export { RpcStreamingClientHost } +export { StreamingSubscribeOptions } +export { WhenClauseContext } // #endregion \ No newline at end of file diff --git a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.js b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.js index bfc1f93c..55fa26d6 100644 --- a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.js +++ b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/client.snapshot.js @@ -2,15 +2,15 @@ * Generated by tsnapi — public API snapshot of `@vitejs/devtools-kit/client` */ // #region Functions -export async function connectRemoteDevTools(_) {} export function getDevToolsClientContext() {} -export function parseRemoteConnection(_) {} // #endregion // #region Variables export var CLIENT_CONTEXT_KEY /* const */ // #endregion -// #region Re-exports -export * from "devframe/client"; +// #region Other +export { connectRemoteDevTools } +export { getDevToolsRpcClient } +export { parseRemoteConnection } // #endregion \ No newline at end of file diff --git a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.d.ts b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.d.ts index 02e84179..55e2e220 100644 --- a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.d.ts +++ b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.d.ts @@ -3,9 +3,18 @@ */ // #region Variables export declare const DEFAULT_CATEGORIES_ORDER: Record; -export declare const DEFAULT_STATE_USER_SETTINGS: () => DevToolsDocksUserSettings; +export declare const DEVTOOLS_DIRNAME: string; +export declare const DEVTOOLS_DOCK_IMPORTS_VIRTUAL_ID: string; +export declare const DEVTOOLS_MOUNT_PATH: string; +export declare const DEVTOOLS_MOUNT_PATH_NO_TRAILING_SLASH: string; // #endregion -// #region Re-exports -export * from "devframe/constants"; +// #region Other +export { DEFAULT_STATE_USER_SETTINGS } +export { DEVTOOLS_CONNECTION_META_FILENAME } +export { DEVTOOLS_DOCK_IMPORTS_FILENAME } +export { DEVTOOLS_RPC_DUMP_DIRNAME } +export { DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME } +export { DevToolsDocksUserSettings } +export { REMOTE_CONNECTION_KEY } // #endregion \ No newline at end of file diff --git a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.js b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.js index ecc6f929..cd456653 100644 --- a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.js +++ b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/constants.snapshot.js @@ -3,9 +3,17 @@ */ // #region Variables export var DEFAULT_CATEGORIES_ORDER /* const */ -export var DEFAULT_STATE_USER_SETTINGS /* const */ +export var DEVTOOLS_DIRNAME /* const */ +export var DEVTOOLS_DOCK_IMPORTS_VIRTUAL_ID /* const */ +export var DEVTOOLS_MOUNT_PATH /* const */ +export var DEVTOOLS_MOUNT_PATH_NO_TRAILING_SLASH /* const */ // #endregion -// #region Re-exports -export * from "devframe/constants"; +// #region Other +export { DEFAULT_STATE_USER_SETTINGS } +export { DEVTOOLS_CONNECTION_META_FILENAME } +export { DEVTOOLS_DOCK_IMPORTS_FILENAME } +export { DEVTOOLS_RPC_DUMP_DIRNAME } +export { DEVTOOLS_RPC_DUMP_MANIFEST_FILENAME } +export { REMOTE_CONNECTION_KEY } // #endregion \ No newline at end of file diff --git a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/index.snapshot.d.ts b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/index.snapshot.d.ts index 27eab3e8..56d0a956 100644 --- a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/index.snapshot.d.ts +++ b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/index.snapshot.d.ts @@ -1,16 +1,6 @@ /** * Generated by tsnapi — public API snapshot of `@vitejs/devtools-kit` */ -// #region Functions -export declare function defineCommand(_: Omit & { - when?: WhenExpression; -}): DevToolsServerCommandInput; -export declare function defineDockEntry(_: Omit & { - when?: WhenExpression; -}): T; -export declare function defineJsonRenderSpec(_: JsonRenderSpec): JsonRenderSpec; -// #endregion - // #region Variables export declare const defineRpcFunction: (definition: _$devframe_rpc0.RpcFunctionDefinition) => _$devframe_rpc0.RpcFunctionDefinition; // #endregion @@ -19,6 +9,9 @@ export declare const defineRpcFunction: >; - capabilities?: DevToolsCapabilities | { - dev?: DevToolsCapabilities | boolean; - build?: DevToolsCapabilities | boolean; + dock?: Partial>; + capabilities?: DevframeCapabilities | { + dev?: DevframeCapabilities | boolean; + build?: DevframeCapabilities | boolean; }; setup?: (_: KitNodeContext) => void | Promise; } @@ -19,83 +19,19 @@ export interface CreateViteDevToolsHostOptions { } // #endregion -// #region Classes -export declare class DevToolsCommandsHost implements DevToolsCommandsHost$1 { - readonly context: KitNodeContext; - readonly commands: DevToolsCommandsHost$1['commands']; - readonly events: DevToolsCommandsHost$1['events']; - constructor(_: KitNodeContext); - register(_: DevToolsServerCommandInput): DevToolsCommandHandle; - unregister(_: string): boolean; - execute(_: string, ..._: any[]): Promise; - list(): DevToolsServerCommandEntry[]; - private findCommand; - private toSerializable; -} -export declare class DevToolsDockHost implements DevToolsDockHost$1 { - readonly context: KitNodeContext; - readonly views: DevToolsDockHost$1['views']; - readonly events: DevToolsDockHost$1['events']; - userSettings: SharedState; - private readonly remoteDocks; - constructor(_: KitNodeContext); - init(): Promise; - values({ - includeBuiltin - }?: { - includeBuiltin?: boolean; - }): DevToolsDockEntry[]; - private projectView; - private resolveDevServerOrigin; - register(_: T, _?: boolean): { - update: (_: Partial) => void; - }; - update(_: DevToolsDockUserEntry): void; - private prepareRemoteRegistration; -} -export declare class DevToolsMessagesHost implements DevToolsMessagesHost$1 { - readonly context: KitNodeContext; - readonly entries: DevToolsMessagesHost$1['entries']; - readonly events: DevToolsMessagesHost$1['events']; - readonly lastModified: Map; - readonly removals: Array<{ - id: string; - time: number; - }>; - private _autoDeleteTimers; - private _clock; - private _tick; - constructor(_: KitNodeContext); - add(_: DevToolsMessageEntryInput): Promise; - update(_: string, _: Partial): Promise; - remove(_: string): Promise; - clear(): Promise; - private _createHandle; -} -export declare class DevToolsTerminalHost implements DevToolsTerminalHost$1 { - readonly context: KitNodeContext; - readonly sessions: DevToolsTerminalHost$1['sessions']; - readonly events: DevToolsTerminalHost$1['events']; - private _boundStreams; - private _channel?; - constructor(_: KitNodeContext); - private getStreamingChannel; - register(_: DevToolsTerminalSession): DevToolsTerminalSession; - update(_: PartialWithoutId): void; - remove(_: DevToolsTerminalSession): void; - private bindStream; - startChildProcess(_: DevToolsChildProcessExecuteOptions, _: Omit): Promise; -} -// #endregion - // #region Functions export declare function createPluginFromDevframe(_: DevframeDefinition, _?: CreatePluginFromDevframeOptions): PluginWithDevTools; export declare function createSimpleClientScript(_: string | ((_: any) => void)): ClientScriptEntry; -export declare function createViteDevToolsHost(_: CreateViteDevToolsHostOptions): DevToolsHost; +export declare function createViteDevToolsHost(_: CreateViteDevToolsHostOptions): DevframeHost; // #endregion // #region Other export { createKitContext } export { CreateKitContextOptions } +export { DevToolsCommandsHost } +export { DevToolsDockHost } +export { DevToolsMessagesHost } +export { DevToolsTerminalHost } export { KitNodeContext } +export { mountDevframe } // #endregion \ No newline at end of file diff --git a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/node.snapshot.js b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/node.snapshot.js index 3fbb88ca..b52bf360 100644 --- a/test/__snapshots__/tsnapi/@vitejs/devtools-kit/node.snapshot.js +++ b/test/__snapshots__/tsnapi/@vitejs/devtools-kit/node.snapshot.js @@ -1,69 +1,17 @@ /** * Generated by tsnapi — public API snapshot of `@vitejs/devtools-kit/node` */ -// #region Classes -export class DevToolsCommandsHost { - context - commands - events - constructor(_) {} - register(_) {} - unregister(_) {} - async execute(_, ..._) {} - list() {} - findCommand(_) {} - toSerializable(_) {} -} -export class DevToolsDockHost { - context - views - events - userSettings - remoteDocks - constructor(_) {} - async init() {} - values(_) {} - projectView(_) {} - resolveDevServerOrigin() {} - register(_, _) {} - update(_) {} - prepareRemoteRegistration(_) {} -} -export class DevToolsMessagesHost { - context - entries - events - lastModified - removals - _autoDeleteTimers - _clock - _tick() {} - constructor(_) {} - async add(_) {} - async update(_, _) {} - async remove(_) {} - async clear() {} - _createHandle(_) {} -} -export class DevToolsTerminalHost { - context - sessions - events - _boundStreams - _channel - constructor(_) {} - getStreamingChannel() {} - register(_) {} - update(_) {} - remove(_) {} - bindStream(_) {} - async startChildProcess(_, _) {} -} -// #endregion - // #region Functions export async function createKitContext(_) {} export function createPluginFromDevframe(_, _) {} export function createSimpleClientScript(_) {} export function createViteDevToolsHost(_) {} +// #endregion + +// #region Other +export { DevToolsCommandsHost } +export { DevToolsDockHost } +export { DevToolsMessagesHost } +export { DevToolsTerminalHost } +export { mountDevframe } // #endregion \ No newline at end of file