Skip to content

Commit

Permalink
feat: isolate ui options per-project (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed May 11, 2023
1 parent 2b64734 commit 3b99477
Show file tree
Hide file tree
Showing 20 changed files with 131 additions and 67 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -104,7 +104,7 @@ export default defineNuxtConfig({
})
```

For all options available, please refer to TSDocs in your IDE, or the [type definition file](https://github.com/nuxt/devtools/blob/main/packages/devtools-kit/src/_types/module-options.ts).
For all options available, please refer to TSDocs in your IDE, or the [type definition file](https://github.com/nuxt/devtools/blob/main/packages/devtools-kit/src/_types/options.ts).

## Features

Expand Down
1 change: 0 additions & 1 deletion package.json
Expand Up @@ -21,7 +21,6 @@
"@nuxt/devtools": "workspace:*",
"@nuxt/devtools-ui-kit": "workspace:*",
"@nuxt/module-builder": "^0.3.1",
"@types/fs-extra": "^11.0.1",
"@types/markdown-it": "^12.2.3",
"@types/node": "^18.16.5",
"@types/pacote": "^11.1.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools-kit/src/_types/index.ts
Expand Up @@ -7,5 +7,5 @@ export * from './integrations'
export * from './wizard'
export * from './rpc'
export * from './server-ctx'
export * from './module-options'
export * from './options'
export * from './common'
Expand Up @@ -87,3 +87,17 @@ export interface VSCodeTunnelOptions {
*/
name?: string
}

export interface NuxtDevToolsUIOptions {
componentsView: 'list' | 'graph'
componentsGraphShowNodeModules: boolean
componentsGraphShowPages: boolean
componentsGraphShowLayouts: boolean
componentsGraphShowWorkspace: boolean
interactionCloseOnOutsideClick: boolean
showExperimentalFeatures: boolean
showHelpButtons: boolean
scale: number
hiddenTabs: string[]
hiddenTabCategories: string[]
}
5 changes: 5 additions & 0 deletions packages/devtools-kit/src/_types/rpc.ts
@@ -1,6 +1,7 @@
import type { Component, NuxtApp, NuxtLayout, NuxtOptions, NuxtPage } from 'nuxt/schema'
import type { StorageMounts } from 'nitropack'
import type { StorageValue } from 'unstorage'
import type { NuxtDevToolsUIOptions } from './options'
import type { ModuleCustomTab } from './custom-tabs'
import type { AssetInfo, AutoImportsWithMetadata, ComponentRelationship, HookInfo, ImageMeta, NpmCommandOptions, NpmCommandType, PackageManagerName, PackageUpdateInfo, ServerRouteInfo } from './integrations'
import type { TerminalAction, TerminalInfo } from './terminals'
Expand All @@ -21,6 +22,10 @@ export interface ServerFunctions {
getServerRoutes(): Promise<ServerRouteInfo[]>
getServerApp(): NuxtApp | undefined

// Options
getUIOptions(): Promise<NuxtDevToolsUIOptions>
updateUIOptions(settings: Partial<NuxtDevToolsUIOptions>): Promise<void>

// Updates
checkForUpdateFor(name: string): Promise<PackageUpdateInfo | undefined>
getPackageManager(): Promise<PackageManagerName>
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools-kit/src/_types/server-ctx.ts
@@ -1,7 +1,7 @@
import type { BirpcGroup } from 'birpc'
import type { Nuxt } from 'nuxt/schema'
import type { ClientFunctions, ServerFunctions } from './rpc'
import type { ModuleOptions } from './module-options'
import type { ModuleOptions } from './options'

/**
* @internal
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/app.vue
Expand Up @@ -42,7 +42,7 @@ addEventListener('keydown', (e) => {
const {
scale,
} = useDevToolsSettings()
} = useDevToolsOptions()
onMounted(() => {
const injectClient = useInjectionClient()
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/components/ComponentsGraph.vue
Expand Up @@ -30,7 +30,7 @@ const {
componentsGraphShowPages: showPages,
componentsGraphShowLayouts: showLayouts,
componentsGraphShowWorkspace: showWorkspace,
} = useDevToolsSettings()
} = useDevToolsOptions()
const selectedFilter = ref<ComponentRelationship>()
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/components/HelpFab.vue
Expand Up @@ -2,7 +2,7 @@
const open = ref(false)
const {
showHelpButtons,
} = useDevToolsSettings()
} = useDevToolsOptions()
</script>

<template>
Expand Down
6 changes: 3 additions & 3 deletions packages/devtools/client/composables/state.ts
Expand Up @@ -85,7 +85,7 @@ export function useTerminals() {

export function useAllTabs() {
const customTabs = useCustomTabs()
const settings = useDevToolsSettings()
const settings = useDevToolsOptions()
const router = useRouter()
const client = useClient()

Expand Down Expand Up @@ -133,7 +133,7 @@ export function useCategorizedTabs(enabledOnly = true) {
? useEnabledTabs()
: useAllTabs()

const settings = useDevToolsSettings()
const settings = useDevToolsOptions()

return computed(() => {
const categories: Record<TabCategory, (ModuleCustomTab | ModuleBuiltinTab)[]> = {
Expand Down Expand Up @@ -161,7 +161,7 @@ export function useCategorizedTabs(enabledOnly = true) {

export function useEnabledTabs() {
const tabs = useAllTabs()
const settings = useDevToolsSettings()
const settings = useDevToolsOptions()

return computed(() => tabs.value.filter((tab) => {
const _tab = tab as ModuleBuiltinTab
Expand Down
40 changes: 21 additions & 19 deletions packages/devtools/client/composables/storage.ts
@@ -1,30 +1,32 @@
import { toRefs } from '@vueuse/core'
import type { DevToolsFrameState, DevToolsUISettings } from '~~/../src/types'
import type { DevToolsFrameState, NuxtDevToolsUIOptions } from '~~/../src/types'

export const isFirstVisit = useLocalStorage('nuxt-devtools-first-visit', true)

const devToolsSettings = useLocalStorage<DevToolsUISettings>('nuxt-devtools-settings', {
componentsView: 'list',
componentsGraphShowNodeModules: false,
componentsGraphShowPages: false,
componentsGraphShowLayouts: false,
componentsGraphShowWorkspace: true,
interactionCloseOnOutsideClick: false,
showExperimentalFeatures: false,
showHelpButtons: true,
scale: 1,
hiddenTabs: [],
hiddenTabCategories: [],
}, { mergeDefaults: true })

const devToolsSettingsRefs = toRefs(devToolsSettings)

const devToolsFrameState = useLocalStorage<DevToolsFrameState>('nuxt-devtools-frame-state', {} as any, { listenToStorageChanges: false })

const devToolsPanelsState = useLocalStorage<Record<string, number>>('nuxt-devtools-panels-state', {} as any, { listenToStorageChanges: false })

export function useDevToolsSettings() {
return devToolsSettingsRefs
const devToolsOptions = ref<NuxtDevToolsUIOptions>(await rpc.getUIOptions())
const devToolsOptionsRefs = toRefs(devToolsOptions)

watch(devToolsOptions, async (options) => {
rpc.updateUIOptions(options)
}, { deep: true, flush: 'post' })

// sync devtools options with frame state
watchEffect(() => {
devToolsFrameState.value.closeOnOutsideClick = devToolsOptions.value.interactionCloseOnOutsideClick
})

// Migrate settings from localStorage to devtools options. TODO: remove in next major release
if (localStorage.getItem('nuxt-devtools-settings')) {
Object.assign(devToolsOptions.value, JSON.parse(localStorage.getItem('nuxt-devtools-settings')!))
localStorage.removeItem('nuxt-devtools-settings')
}

export function useDevToolsOptions() {
return devToolsOptionsRefs
}

export function useDevToolsFrameState() {
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/pages/modules/components.vue
Expand Up @@ -15,7 +15,7 @@ const components = useComponents()
const {
componentsView: view,
} = useDevToolsSettings()
} = useDevToolsOptions()
function openComponentInspector() {
if (!client.value?.inspector?.instance)
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/pages/modules/modules.vue
Expand Up @@ -20,7 +20,7 @@ const modules = computed(() => config.value?._installedModules || [])
const packageModules = ref<any[]>([])
const userModules = ref<any[]>([])
const installModuleOpen = ref(false)
const { showExperimentalFeatures } = useDevToolsSettings()
const { showExperimentalFeatures } = useDevToolsOptions()
watchEffect(() => {
packageModules.value.length = 0
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/client/pages/settings.vue
Expand Up @@ -10,7 +10,7 @@ const {
scale,
hiddenTabs,
hiddenTabCategories,
} = useDevToolsSettings()
} = useDevToolsOptions()
const scaleOptions = [
['Tiny', 12 / 15],
Expand Down
4 changes: 2 additions & 2 deletions packages/devtools/src/runtime/plugins/view/Frame.vue
Expand Up @@ -3,7 +3,7 @@ import type { VueInspectorClient } from 'vite-plugin-vue-inspector'
import type { PropType } from 'vue'
import { computed, ref, watch, watchEffect } from 'vue'
import type { NuxtDevtoolsHostClient, NuxtDevtoolsIframeClient, NuxtDevtoolsGlobal as NuxtDevtoolsViewGlobal } from '../../../types'
import { PANEL_MAX, PANEL_MIN, PANEL_PADDING, closePanel, settings, state, viewMode } from './state'
import { PANEL_MAX, PANEL_MIN, PANEL_PADDING, closePanel, state, viewMode } from './state'
import { useEventListener } from './utils'
// Can't use reactivity transform here because this file is shipped as-is,
Expand Down Expand Up @@ -189,7 +189,7 @@ useEventListener(window, 'keydown', (e: KeyboardEvent) => {
// Close panel on outside click (when enabled)
useEventListener(window, 'mousedown', (e: MouseEvent) => {
if (!settings.value.interactionCloseOnOutsideClick)
if (!state.value.closeOnOutsideClick)
return
if (!state.value.open || isDragging.value || viewMode.value !== 'default')
return
Expand Down
5 changes: 2 additions & 3 deletions packages/devtools/src/runtime/plugins/view/state.ts
@@ -1,5 +1,5 @@
import { ref } from 'vue'
import type { DevToolsFrameState, DevToolsUISettings } from '../../../types'
import type { DevToolsFrameState } from '../../../types'
import { useObjectStorage } from './utils'

export const PANEL_PADDING = 10
Expand All @@ -18,10 +18,9 @@ export const state = useObjectStorage<DevToolsFrameState>('nuxt-devtools-frame-s
open: false,
route: '/',
position: 'bottom',
closeOnOutsideClick: false,
})

export const settings = useObjectStorage<DevToolsUISettings>('nuxt-devtools-settings', {} as any, true)

export function togglePanel() {
if (state.value.open)
closePanel()
Expand Down
2 changes: 2 additions & 0 deletions packages/devtools/src/server-rpc/index.ts
Expand Up @@ -15,6 +15,7 @@ import { setupGeneralRPC } from './general'
import { setupWizardRPC } from './wizard'
import { setupTerminalRPC } from './terminals'
import { setupServerRoutesRPC } from './server-routes'
import { setupUIOptionsRPC } from './ui-options'

export function setupRPC(nuxt: Nuxt, options: ModuleOptions) {
const serverFunctions = {} as ServerFunctions
Expand Down Expand Up @@ -78,6 +79,7 @@ export function setupRPC(nuxt: Nuxt, options: ModuleOptions) {
...setupWizardRPC(ctx),
...setupTerminalRPC(ctx),
...setupServerRoutesRPC(ctx),
...setupUIOptionsRPC(ctx),
} satisfies ServerFunctions)

const wsClients = new Set<WebSocket>()
Expand Down
72 changes: 72 additions & 0 deletions packages/devtools/src/server-rpc/ui-options.ts
@@ -0,0 +1,72 @@
import { homedir } from 'node:os'
import { existsSync } from 'node:fs'
import fs from 'node:fs/promises'
import { hash } from 'ohash'
import { join } from 'pathe'
import type { NuxtDevToolsUIOptions, NuxtDevtoolsServerContext, ServerFunctions } from '../types'

const defaults: NuxtDevToolsUIOptions = {
componentsView: 'list',
componentsGraphShowNodeModules: false,
componentsGraphShowPages: false,
componentsGraphShowLayouts: false,
componentsGraphShowWorkspace: true,
interactionCloseOnOutsideClick: false,
showExperimentalFeatures: false,
showHelpButtons: true,
scale: 1,
hiddenTabs: [],
hiddenTabCategories: [],
}

export function setupUIOptionsRPC({ nuxt }: NuxtDevtoolsServerContext) {
const home = homedir()
const projectHash = hash(nuxt.options.rootDir)
const dir = join(home, '.nuxt/devtools')
const filepath = join(dir, `${projectHash}.json`)

let settings: NuxtDevToolsUIOptions | undefined

async function getUIOptions() {
if (!settings)
await read()
return settings!
}

async function read() {
if (existsSync(filepath)) {
settings = {
...defaults,
...JSON.parse(await fs.readFile(filepath, 'utf-8')).settings || {},
}
}
else {
settings = { ...defaults }
}
return settings!
}

async function write(settings: NuxtDevToolsUIOptions) {
if (!existsSync(dir))
await fs.mkdir(dir, { recursive: true })

await fs.writeFile(
filepath,
JSON.stringify({
root: nuxt.options.rootDir,
hash: projectHash,
settings,
}, null, 2),
'utf-8',
)
}

return {
async updateUIOptions(_settings) {
const settings = await getUIOptions()
Object.assign(settings, _settings)
await write(settings)
},
getUIOptions,
} satisfies Partial<ServerFunctions>
}
15 changes: 1 addition & 14 deletions packages/devtools/src/types/ui-state.ts
Expand Up @@ -6,20 +6,7 @@ export interface DevToolsFrameState {
open: boolean
route: string
position: 'left' | 'right' | 'bottom' | 'top'
}

export interface DevToolsUISettings {
componentsView: 'list' | 'graph'
componentsGraphShowNodeModules: boolean
componentsGraphShowPages: boolean
componentsGraphShowLayouts: boolean
componentsGraphShowWorkspace: boolean
interactionCloseOnOutsideClick: boolean
showExperimentalFeatures: boolean
showHelpButtons: boolean
scale: number
hiddenTabs: string[]
hiddenTabCategories: string[]
closeOnOutsideClick: boolean
}

export interface SocialPreviewResolved {
Expand Down
16 changes: 0 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3b99477

Please sign in to comment.