Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/core/src/client/standalone/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
<script setup lang="ts">
import type { DocksContext } from '@vitejs/devtools-kit/client'
import { CLIENT_CONTEXT_KEY, getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
import { watchEffect } from 'vue'
import DockStandalone from '../webcomponents/components/dock/DockStandalone.vue'
import { isDark } from '../webcomponents/state/color-mode'
import { createDocksContext } from '../webcomponents/state/context'

// Standalone runs in the light DOM, so the page canvas (html) sits behind the
// full-screen shell — mirror the color mode onto it so its background and
// native controls follow the Auto/Light/Dark choice.
watchEffect(() => {
const el = document.documentElement
el.classList.toggle('dark', isDark.value)
el.classList.toggle('light', !isDark.value)
el.style.colorScheme = isDark.value ? 'dark' : 'light'
})

const rpc = await getDevToolsRpcClient()

// eslint-disable-next-line no-console
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/client/webcomponents/.generated/css.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ function getKeybindings(id: string) {
>
<ViteDevToolsLogo class="absolute top--32px left-5px w-60 pointer-events-none" />
<div
class="w-full w-lg bg-glass:75 color-base border border-base rounded-lg shadow-xl pointer-events-auto of-hidden flex flex-col max-h-[60vh]"
class="w-full w-lg bg-glass:80 color-base border border-base rounded-lg shadow-xl pointer-events-auto of-hidden flex flex-col max-h-[60vh]"
>
<!-- Header -->
<header class="border-b border-base flex items-center px-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function openMessages(toastId: string) {
<div
v-for="toast of toasts"
:key="toast.id"
class="bg-glass border color-base border-base shadow-xl cursor-pointer hover:bg-active transition-colors rounded"
class="bg-glass:80 border color-base border-base shadow-xl cursor-pointer hover:bg-active transition-colors rounded"
@click="openMessages(toast.id)"
>
<MessageItem :entry="toast.entry" compact class="px-3 py-2.5">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import { isDark } from '../../state/color-mode'
</script>

<template>
<!--
Carries the `.dark`/`.light` class that class-based UnoCSS utilities resolve
against. `display: contents` keeps it out of the box tree, so the shell's
fixed/absolute-positioned children (dock anchor, floating layers) are
unaffected while every descendant still matches `.dark …` selectors — inside
the dock's shadow root and the light-DOM standalone page alike. The native
`color-scheme` inherits down for scrollbars and form controls.
-->
<div
class="vite-devtools-color-root"
:class="isDark ? 'dark' : 'light'"
:style="{ display: 'contents', colorScheme: isDark ? 'dark' : 'light' }"
>
<slot />
</div>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ const contentClass = computed(() => {
<template>
<div
id="vite-devtools-edge-panel"
class="bg-glass:75 border border-base color-base shadow overflow-hidden z-floating-anchor font-sans text-[15px] box-border"
class="bg-glass:80 border border-base color-base shadow overflow-hidden z-floating-anchor font-sans text-[15px] box-border"
:class="panelLayoutClass"
:style="panelStyle"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CommandPalette from '../command-palette/CommandPalette.vue'
import Confirm from '../display/Confirm.vue'
import ToastOverlay from '../display/ToastOverlay.vue'
import FloatingElements from '../floating/FloatingElements.vue'
import ColorSchemeRoot from './ColorSchemeRoot.vue'
import Dock from './Dock.vue'
import DockEdge from './DockEdge.vue'
import DockPanel from './DockPanel.vue'
Expand Down Expand Up @@ -52,26 +53,28 @@ onUnmounted(() => {
</script>

<template>
<template v-if="!isDockPopupOpen">
<template v-if="isRpcTrusted && context.panel.store.mode === 'edge'">
<DockEdge :context />
<ColorSchemeRoot>
<template v-if="!isDockPopupOpen">
<template v-if="isRpcTrusted && context.panel.store.mode === 'edge'">
<DockEdge :context />
</template>
<template v-else>
<Dock :context :layout="props.layout">
<template #default="{ dockEl, panelMargins, selected, layout }">
<DockPanel
:context
:selected
:dock-el="dockEl!"
:panel-margins="panelMargins"
:layout="layout"
/>
</template>
</Dock>
</template>
<FloatingElements />
</template>
<template v-else>
<Dock :context :layout="props.layout">
<template #default="{ dockEl, panelMargins, selected, layout }">
<DockPanel
:context
:selected
:dock-el="dockEl!"
:panel-margins="panelMargins"
:layout="layout"
/>
</template>
</Dock>
</template>
<FloatingElements />
</template>
<CommandPalette v-if="!isDockPopupOpen" :context />
<ToastOverlay :context />
<Confirm v-if="!isDockPopupOpen" />
<CommandPalette v-if="!isDockPopupOpen" :context />
<ToastOverlay :context />
<Confirm v-if="!isDockPopupOpen" />
</ColorSchemeRoot>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ onMounted(() => {
<div
v-show="context.docks.selected && context.docks.selected.type !== 'action'"
ref="dockPanel"
class="bg-glass:75 rounded-lg border border-base color-base shadow overflow-hidden"
class="bg-glass:80 rounded-lg border border-base color-base shadow overflow-hidden"
:style="panelStyle"
@contextmenu="openContextMenu"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import FloatingElements from '../floating/FloatingElements.vue'
import VitePlus from '../icons/VitePlus.vue'
import ViewBuiltinClientAuthNotice from '../views-builtin/ViewBuiltinClientAuthNotice.vue'
import ViewEntry from '../views/ViewEntry.vue'
import ColorSchemeRoot from './ColorSchemeRoot.vue'
import DockEntriesWithCategories from './DockEntriesWithCategories.vue'
import DockGroupSidebar from './DockGroupSidebar.vue'

Expand Down Expand Up @@ -47,50 +48,52 @@ function switchEntry(id: string | undefined) {
</script>

<template>
<div v-if="!isRpcTrusted" class="h-screen w-screen of-hidden">
<ViewBuiltinClientAuthNotice :context="context" />
</div>
<div v-else class="h-screen w-screen of-hidden grid cols-[max-content_1fr]">
<div class="border-r border-base flex flex-col min-h-0">
<div class="p2 border-b border-base flex">
<VitePlus class="w-7 h-7 ma" />
</div>
<div class="transition duration-200 p2 of-y-auto">
<DockEntriesWithCategories
:context="context"
:groups="groupedEntries"
:is-vertical="false"
:selected="context.docks.selected"
:dim-inactive="false"
@select="(e) => switchEntry(e?.id)"
>
<template #separator>
<div class="border-base border-b w-full my-2" />
</template>
</DockEntriesWithCategories>
</div>
<ColorSchemeRoot>
<div v-if="!isRpcTrusted" class="h-screen w-screen of-hidden">
<ViewBuiltinClientAuthNotice :context="context" />
</div>
<div class="min-h-0 flex">
<DockGroupSidebar
v-if="activeGroup"
:context
:group="activeGroup"
:selected-id="context.docks.selected?.id ?? null"
/>
<div class="relative flex-1 min-w-0 min-h-0">
<div id="vite-devtools-views-container" ref="viewsContainer" class="pointer-events-auto" />
<ViewEntry
v-if="context.docks.selected && panes"
:key="context.docks.selected.id"
:entry="context.docks.selected"
<div v-else class="h-screen w-screen of-hidden grid cols-[max-content_1fr]">
<div class="border-r border-base flex flex-col min-h-0">
<div class="p2 border-b border-base flex">
<VitePlus class="w-7 h-7 ma" />
</div>
<div class="transition duration-200 p2 of-y-auto">
<DockEntriesWithCategories
:context="context"
:groups="groupedEntries"
:is-vertical="false"
:selected="context.docks.selected"
:dim-inactive="false"
@select="(e) => switchEntry(e?.id)"
>
<template #separator>
<div class="border-base border-b w-full my-2" />
</template>
</DockEntriesWithCategories>
</div>
</div>
<div class="min-h-0 flex">
<DockGroupSidebar
v-if="activeGroup"
:context
:panes="panes"
:group="activeGroup"
:selected-id="context.docks.selected?.id ?? null"
/>
<div class="relative flex-1 min-w-0 min-h-0">
<div id="vite-devtools-views-container" ref="viewsContainer" class="pointer-events-auto" />
<ViewEntry
v-if="context.docks.selected && panes"
:key="context.docks.selected.id"
:entry="context.docks.selected"
:context
:panes="panes"
/>
</div>
</div>
</div>
</div>
<FloatingElements />
<CommandPalette :context />
<ToastOverlay :context />
<Confirm />
<FloatingElements />
<CommandPalette :context />
<ToastOverlay :context />
<Confirm />
</ColorSchemeRoot>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ export interface DockMargins {

/** The built-in dock layout. Override individual fields via {@link resolveDockLayout}. */
export const DEFAULT_DOCK_LAYOUT: DockLayout = Object.freeze({
barHeight: 35,
barHeight: 34,
barMinWidth: 100,
minimizedSize: 22,
glowSize: 160,
glowBlur: 60,
maxVisibleItems: 5,
viewportMargin: 2,
panelOverlapFactor: 0.2,
panelOverlapFactor: -0.04,
edgeSnapPercent: 5,
centerSnapPercent: 2,
edgeZoneHeight: 70,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const FloatingPopoverComponent = defineComponent({
{
ref: 'panel',
class: [
`fixed z-floating-tooltip text-xs ${transitionClass} w-max bg-glass color-base border border-base rounded px2 p1`,
`fixed z-floating-tooltip text-xs ${transitionClass} w-max bg-glass:80 color-base border border-base rounded px2 p1`,
'op0 pointer-events-none',
props.panelClass,
],
Expand Down Expand Up @@ -162,7 +162,7 @@ const FloatingPopoverComponent = defineComponent({
{
ref: 'panel',
class: [
`fixed z-floating-tooltip text-xs ${transitionClass} w-max bg-glass color-base border border-base rounded px2 p1`,
`fixed z-floating-tooltip text-xs ${transitionClass} w-max bg-glass:80 color-base border border-base rounded px2 p1`,
props.item ? 'op100' : 'op0 pointer-events-none',
props.panelClass,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script setup lang="ts">
import { usePreferredDark } from '@vueuse/core'
import { computed } from 'vue'
import { isDark } from '../../state/color-mode'

const isDark = usePreferredDark()
const theme = computed(() => isDark.value ? 'white' : 'black')
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DocksContext } from '@vitejs/devtools-kit/client'
import type { SharedState } from 'devframe/utils/shared-state'
import type { DevToolsDocksUserSettings } from '../../state/dock-settings'
import { computed } from 'vue'
import { colorSchemePreference, setColorSchemePreference } from '../../state/color-mode'
import { sharedStateToRef } from '../../state/docks'
import { isDockPopupSupported, requestDockPopupOpen, useIsDockPopupOpen } from '../../state/popup'

Expand All @@ -29,6 +30,12 @@ const dockModeOptions = computed(() => {

const currentDockMode = computed(() => panelStore.mode)

const colorModeOptions = [
{ value: 'auto', label: 'Auto', icon: 'i-ph-laptop-duotone' },
{ value: 'light', label: 'Light', icon: 'i-ph-sun-duotone' },
{ value: 'dark', label: 'Dark', icon: 'i-ph-moon-duotone' },
] as const

function setDockMode(mode: string) {
if (mode === 'popup') {
requestDockPopupOpen(props.context)
Expand All @@ -41,6 +48,28 @@ function setDockMode(mode: string) {

<template>
<div class="flex flex-col gap-4">
<!-- Color mode -->
<div class="flex flex-col gap-2">
<div class="flex flex-col">
<span class="text-sm">Color mode</span>
<span class="text-xs op50">Theme for DevTools and its inner panels</span>
</div>
<div class="flex items-center gap-1 bg-gray/10 rounded-lg p1 w-fit">
<button
v-for="option of colorModeOptions"
:key="option.value"
class="flex items-center gap-1.5 px3 py1.5 rounded-md text-sm transition-all"
:class="colorSchemePreference === option.value
? 'bg-base shadow text-primary font-medium'
: 'op60 hover:op100 hover:bg-gray/10'"
@click="setColorSchemePreference(option.value)"
>
<div :class="option.icon" class="w-4 h-4" />
{{ option.label }}
</button>
</div>
</div>

<!-- Dock mode -->
<div v-if="isEmbedded && !isDockPopupOpen" class="flex flex-col gap-2">
<div class="flex flex-col">
Expand Down
31 changes: 31 additions & 0 deletions packages/core/src/client/webcomponents/state/color-mode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { usePreferredDark, useStorage } from '@vueuse/core'
import { computed } from 'vue'

export type ColorSchemePreference = 'auto' | 'light' | 'dark'

/**
* Shared with the Nuxt plugin apps (`@vitejs/devtools-ui/composables/dark`).
* Those apps are served same-origin under the DevTools mount path, so writing
* this key here propagates into the iframes via `storage` events — one switch
* drives the dock shell and every inner integration at once.
*/
export const COLOR_SCHEME_STORAGE_KEY = 'vite-devtools-color-scheme'

/** Persisted Auto / Light / Dark choice, defaulting to Auto (follow the OS). */
export const colorSchemePreference = useStorage<ColorSchemePreference>(
COLOR_SCHEME_STORAGE_KEY,
'auto',
)

const preferredDark = usePreferredDark()

/** Resolved dark state — `auto` defers to the OS `prefers-color-scheme`. */
export const isDark = computed(() =>
colorSchemePreference.value === 'auto'
? preferredDark.value
: colorSchemePreference.value === 'dark',
)

export function setColorSchemePreference(preference: ColorSchemePreference) {
colorSchemePreference.value = preference
}
Loading
Loading