Skip to content

Commit

Permalink
feat: add settings for devtools plugin (#597)
Browse files Browse the repository at this point in the history
  • Loading branch information
webfansplz authored Sep 3, 2024
1 parent 344987a commit 77c0bc7
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 36 deletions.
59 changes: 59 additions & 0 deletions packages/applet/src/components/settings/Settings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<script setup lang="ts">
import { VueSelect, VueSwitch } from '@vue/devtools-ui'
import { rpc } from '@vue/devtools-core'
import { useCustomInspectorState } from '~/composables/custom-inspector-state'
const props = defineProps<{
pluginId: string
options: Record<string, unknown>
values: Record<string, unknown>
}>()
const emit = defineEmits(['update'])
const state = useCustomInspectorState()
const options = computed(() => props.options)
const values = computed(() => props.values)
function toggleOption(key: string, v: unknown) {
rpc.value.updatePluginSettings(props.pluginId, key, v)
rpc.value.getPluginSettings(props.pluginId).then((_settings) => {
emit('update', _settings)
})
}
</script>

<template>
<div class="flex-1 overflow-y-auto p2">
<ul>
<li v-for="(item, index) in options" :key="index" class="flex items-center py-2">
<div class="max-w-[190px] flex-1 select-none py-1.5 text-sm">
{{ item.label }}
</div>
<div class="w-4/5">
<div v-if="item.type === 'boolean'" class="flex justify-start">
<VueSwitch
:model-value="values[index]"
class="row-reverse flex hover:bg-active py1 pl2 pr1"
@update:model-value="(v: boolean) => toggleOption(index, v)"
>
<div flex="~ gap-2" flex-auto items-center justify-start>
<span capitalize op75>{{ name }}</span>
</div>
</VueSwitch>
</div>
<template v-else-if="item.type === 'choice'">
<div>
<VueSelect
:model-value="values[index]"
:options="item.options"
@update:model-value="(v: string) => toggleOption(index, v)"
/>
</div>
</template>
<template v-else-if="item.type === 'text'">
<VueInput :model-value="values[index]" @update:model-value="(v: string) => toggleOption(index, v)" />
</template>
</div>
</li>
</ul>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import Settings from '~/components/settings/Settings.vue'
import { useCustomInspectorState } from '~/composables/custom-inspector-state'
import Navbar from '~/components/basic/Navbar.vue'
import DevToolsHeader from '~/components/basic/DevToolsHeader.vue'
const settings = inject('pluginSettings')
const customInspectState = useCustomInspectorState()
const options = computed(() => settings.value.options)
const values = computed(() => settings.value.values)
function update(_settings) {
settings.value = _settings
}
</script>

<template>
<div class="h-full flex flex-col">
<DevToolsHeader :doc-link="customInspectState.homepage!">
<Navbar />
</DevToolsHeader>
<Settings :plugin-id="customInspectState.id" :options="options" :values="values" @update="update" />
</div>
</template>
18 changes: 18 additions & 0 deletions packages/applet/src/modules/custom-inspector/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
import { onRpcConnected, rpc } from '@vue/devtools-core'
import About from './components/About.vue'
import Settings from './components/Settings.vue'
import State from './components/state/Index.vue'
import Timeline from './components/timeline/Index.vue'
import AppConnecting from '~/components/basic/AppConnecting.vue'
Expand All @@ -17,6 +18,9 @@ const emit = defineEmits(['loadError'])
const inspectorState = createCustomInspectorStateContext()
const loading = ref(false)
const pluginSettings = ref(null)
provide('pluginSettings', pluginSettings)
const routes = computed(() => {
return [
{
Expand All @@ -36,6 +40,12 @@ const routes = computed(() => {
name: 'About',
component: About,
},
pluginSettings.value && ({
path: '/settings',
name: 'Settings',
component: Settings,
icon: 'i-mdi:cog-outline',
}),
].filter(Boolean) as VirtualRoute[]
})
Expand All @@ -62,6 +72,14 @@ function getInspectorInfo() {
restoreRouter()
loading.value = false
})
rpc.value.getPluginSettings(props.id).then((settings) => {
if (settings.options) {
pluginSettings.value = settings
}
else {
pluginSettings.value = null
}
})
})
}
Expand Down
23 changes: 23 additions & 0 deletions packages/applet/src/modules/pinia/components/Settings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import Settings from '~/components/settings/Settings.vue'
import Navbar from '~/components/basic/Navbar.vue'
import DevToolsHeader from '~/components/basic/DevToolsHeader.vue'
const settings = inject('pluginSettings')
const options = computed(() => settings.value.options)
const values = computed(() => settings.value.values)
const inspectorId = 'pinia'
function update(_settings) {
settings.value = _settings
}
</script>

<template>
<div class="h-full flex flex-col">
<DevToolsHeader doc-link="https://pinia.vuejs.org/" github-repo-link="https://github.com/vuejs/pinia">
<Navbar />
</DevToolsHeader>
<Settings :plugin-id="inspectorId" :options="options" :values="values" @update="update" />
</div>
</template>
66 changes: 46 additions & 20 deletions packages/applet/src/modules/pinia/index.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,57 @@
<script setup lang="ts">
import { onRpcConnected, rpc } from '@vue/devtools-core'
import About from './components/About.vue'
import Store from './components/store/Index.vue'
import Timeline from './components/timeline/Index.vue'
import Settings from './components/Settings.vue'
import { registerVirtualRouter } from '~/composables/virtual-router'
const { VirtualRouterView } = registerVirtualRouter([
{
path: '/store',
name: 'Store',
component: Store,
icon: 'i-carbon-tree-view-alt',
},
{
path: '/timeline',
name: 'Timeline',
component: Timeline,
icon: 'i-mdi:timeline-clock-outline',
},
{
path: '/',
name: 'About',
component: About,
icon: 'i-logos-pinia',
},
], {
const pluginSettings = ref(null)
provide('pluginSettings', pluginSettings)
const routes = computed(() => {
return [
{
path: '/store',
name: 'Store',
component: Store,
icon: 'i-carbon-tree-view-alt',
},
{
path: '/timeline',
name: 'Timeline',
component: Timeline,
icon: 'i-mdi:timeline-clock-outline',
},
{
path: '/',
name: 'About',
component: About,
icon: 'i-logos-pinia',
},
pluginSettings.value && ({
path: '/settings',
name: 'Settings',
component: Settings,
icon: 'i-mdi:cog-outline',
}),
].filter(Boolean) as VirtualRoute[]
})
const { VirtualRouterView, restoreRouter } = registerVirtualRouter(routes, {
defaultRoutePath: '/store',
})
onRpcConnected(() => {
rpc.value.getPluginSettings('pinia').then((settings) => {
if (settings.options) {
pluginSettings.value = settings
}
else {
pluginSettings.value = null
}
})
})
</script>

<template>
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/rpc/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ export const functions = {
async toggleApp(id: string) {
return devtools.ctx.api.toggleApp(id)
},
updatePluginSettings(pluginId: string, key: string, value: string) {
return devtools.ctx.api.updatePluginSettings(pluginId, key, value)
},
getPluginSettings(pluginId: string) {
return devtools.ctx.api.getPluginSettings(pluginId)
},
getRouterInfo() {
return devtoolsRouterInfo
},
Expand Down
21 changes: 6 additions & 15 deletions packages/devtools-kit/src/api/v6/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { DevtoolsContext } from '../../ctx'
import type { App, ComponentBounds, ComponentInstance, CustomInspectorOptions, DevToolsPlugin, TimelineEventOptions, TimelineLayerOptions } from '../../types'
import { DevToolsContextHookKeys, DevToolsV6PluginAPIHookKeys, DevToolsV6PluginAPIHooks } from '../../ctx/hook'
import { devtoolsPluginBuffer } from '../../ctx/plugin'
import { devtoolsHooks } from '../../hook'
import { DevToolsHooks } from '../../types'
import { getActiveInspectors } from '../../ctx/inspector'
import { getPluginSettings, initPluginSettings } from '../../core/plugin/plugin-settings'

export class DevToolsV6PluginAPI {
private plugin: DevToolsPlugin
Expand Down Expand Up @@ -78,6 +78,9 @@ export class DevToolsV6PluginAPI {
// custom inspector
addInspector(options: CustomInspectorOptions) {
this.hooks.callHook(DevToolsContextHookKeys.ADD_INSPECTOR, { inspector: options, plugin: this.plugin })
if (this.plugin.descriptor.settings) {
initPluginSettings(options.id, this.plugin.descriptor.settings)
}
}

sendInspectorTree(inspectorId: string) {
Expand Down Expand Up @@ -107,21 +110,9 @@ export class DevToolsV6PluginAPI {

// settings
getSettings(pluginId?: string) {
function _getSettings(settings) {
const _settings = {}
Object.keys(settings!).forEach((key) => {
_settings[key] = settings![key].defaultValue
})
const inspector = getActiveInspectors().find(i => i.packageName === this.plugin.descriptor.packageName)

return _settings
}
if (pluginId) {
const item = devtoolsPluginBuffer.find(item => item[0].id === pluginId)?.[0] ?? null
return _getSettings(item?.settings) ?? _getSettings(this.plugin.descriptor.settings)
}
else {
return _getSettings(this.plugin.descriptor.settings)
}
return getPluginSettings((pluginId ?? inspector?.id)!, this.plugin.descriptor.settings)
}

// utilities
Expand Down
10 changes: 9 additions & 1 deletion packages/devtools-kit/src/core/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { target } from '@vue/devtools-shared'
import { App, PluginDescriptor, PluginSetupFunction } from '../../types'
import { hook } from '../../hook'
import { devtoolsContext, devtoolsPluginBuffer } from '../../ctx'
import { devtoolsContext, devtoolsInspector, devtoolsPluginBuffer } from '../../ctx'
import { DevToolsPluginAPI } from '../../api'
import { initPluginSettings } from '../../core/plugin/plugin-settings'

export * from './components'

Expand Down Expand Up @@ -33,6 +34,13 @@ export function callDevToolsPluginSetupFn(plugin: [PluginDescriptor, PluginSetup
}

setupFn(api)
if (pluginDescriptor.settings) {
const inspector = devtoolsInspector.find(inspector => inspector.descriptor.id === pluginDescriptor.id)
if (inspector) {
inspector.descriptor.settings = pluginDescriptor.settings
initPluginSettings(inspector.options.id, pluginDescriptor.settings)
}
}
}

export function removeRegisteredPluginApp(app: App) {
Expand Down
68 changes: 68 additions & 0 deletions packages/devtools-kit/src/core/plugin/plugin-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { devtoolsPluginBuffer } from '../../ctx/plugin'
import { PluginDescriptor } from '../../types'
import { DevToolsV6PluginAPIHookKeys, devtoolsContext, getInspector } from '../../ctx'

function _getSettings(settings: PluginDescriptor['settings']) {
const _settings = {}
Object.keys(settings!).forEach((key) => {
_settings[key] = settings![key].defaultValue
})

return _settings
}

function getPluginLocalKey(pluginId: string) {
return `__VUE_DEVTOOLS_NEXT_PLUGIN_SETTINGS__${pluginId}__`
}

export function getPluginSettingsOptions(pluginId: string) {
const descriptor = getInspector(pluginId)?.descriptor
const item = devtoolsPluginBuffer.find(item => item[0].id === descriptor?.id)?.[0] ?? null
return item?.settings ?? null
}

export function getPluginSettings(pluginId: string, fallbackValue?: PluginDescriptor['settings']) {
const localKey = getPluginLocalKey(pluginId)
if (localKey) {
const localSettings = localStorage.getItem(localKey)
if (localSettings) {
return JSON.parse(localSettings)
}
}

if (pluginId) {
const item = devtoolsPluginBuffer.find(item => item[0].id === pluginId)?.[0] ?? null
return _getSettings(item?.settings ?? {})
}
return _getSettings(fallbackValue)
}

export function initPluginSettings(pluginId: string, settings: PluginDescriptor['settings']) {
const localKey = getPluginLocalKey(pluginId)
const localSettings = localStorage.getItem(localKey)
if (!localSettings) {
localStorage.setItem(localKey, JSON.stringify(_getSettings(settings)))
}
}

export function setPluginSettings(pluginId: string, key: string, value: string) {
const localKey = getPluginLocalKey(pluginId)
const localSettings = localStorage.getItem(localKey)!
const parsedLocalSettings = JSON.parse(localSettings || '{}')
const updated = {
...parsedLocalSettings,
[key]: value,
}
localStorage.setItem(localKey, JSON.stringify(updated))

// @ts-expect-error hookable
devtoolsContext.hooks.callHookWith((callbacks) => {
callbacks.forEach(cb => cb({
pluginId,
key,
oldValue: parsedLocalSettings[key],
newValue: value,
settings: updated,
}))
}, DevToolsV6PluginAPIHookKeys.SET_PLUGIN_SETTINGS)
}
Loading

0 comments on commit 77c0bc7

Please sign in to comment.