Skip to content

Commit

Permalink
feat: use Ic icon on custom inspector/tab/command (#466)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex <alexzhang1030@foxmail.com>
Co-authored-by: Alex <49969959+alexzhang1030@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 28, 2024
1 parent c753696 commit bc97b34
Show file tree
Hide file tree
Showing 24 changed files with 2,398 additions and 110 deletions.
19 changes: 11 additions & 8 deletions docs/plugins/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ $ bun add -D @vue/devtools-api

## `addCustomTab`

You can choose any icon from [Material Design Icons](https://fonts.google.com/icons) or [Iconify Ic Baseline](https://icones.netlify.app/collection/ic?variant=Baseline), for example `star`.

```ts
import { addCustomTab } from '@vue/devtools-api'

Expand All @@ -39,8 +41,8 @@ addCustomTab({
name: 'vue-use',
// title to display in the tab
title: 'VueUse',
// any icon from Iconify, or a URL to an image
icon: 'i-logos-vueuse',
// any icon from material design icons or a URL to an image
icon: 'https://vueuse.org/favicon.svg',
// iframe view
view: {
type: 'iframe',
Expand All @@ -52,6 +54,8 @@ addCustomTab({

## `addCustomCommand`

You can choose any icon from [Material Design Icons](https://fonts.google.com/icons) or [Iconify Ic Baseline](https://icones.netlify.app/collection/ic?variant=Baseline), for example `star`.

```ts
import { addCustomCommand } from '@vue/devtools-api'

Expand All @@ -61,8 +65,8 @@ addCustomCommand({
id: 'vueuse',
// title to display in the command
title: 'VueUse',
// any icon from Iconify, or a URL to an image
icon: 'i-logos-vueuse',
// any icon from material design icons or a URL to an image
icon: 'https://vueuse.org/favicon.svg',
action: {
type: 'url',
src: 'https://vueuse.org/'
Expand All @@ -75,14 +79,13 @@ addCustomCommand({
id: 'vueuse',
// title to display in the command
title: 'VueUse',
// any icon from Iconify, or a URL to an image
icon: 'i-logos-vueuse',
// any icon from material design icons or a URL to an image
icon: 'https://vueuse.org/favicon.svg',
// submenu, which is shown when the menu is clicked
children: [
{
id: 'vueuse:github',
title: 'Github',
icon: 'i-carbon-logo-github',
action: {
type: 'url',
src: 'https://github.com/vueuse/vueuse'
Expand All @@ -91,7 +94,7 @@ addCustomCommand({
{
id: 'vueuse:website',
title: 'Website',
icon: 'i-logos-vueuse',
icon: 'auto-awesome',
action: {
type: 'url',
src: 'https://vueuse.org/'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Pane, Splitpanes } from 'splitpanes'
import { DevToolsMessagingEvents, onRpcConnected, rpc } from '@vue/devtools-core'
import { parse } from '@vue/devtools-kit'
import type { CustomInspectorNode, CustomInspectorOptions, CustomInspectorState } from '@vue/devtools-kit'
import { vTooltip } from '@vue/devtools-ui'
import { VueIcIcon, vTooltip } from '@vue/devtools-ui'
import { until } from '@vueuse/core'
import Navbar from '~/components/basic/Navbar.vue'
import DevToolsHeader from '~/components/basic/DevToolsHeader.vue'
Expand Down Expand Up @@ -186,7 +186,7 @@ onUnmounted(() => {
<div v-if="actions?.length" class="mb-1 flex justify-end pb-1" border="b dashed base">
<div class="flex items-center gap-2 px-1">
<div v-for="(action, index) in actions" :key="index" v-tooltip.bottom-end="{ content: action.tooltip }" class="flex items-center gap1" @click="callAction(index)">
<i :class="`i-ic-baseline-${action.icon.replace(/\_/g, '-')}`" cursor-pointer op70 text-base hover:op100 />
<VueIcIcon :name="`baseline-${action.icon.replace(/\_/g, '-')}`" cursor-pointer op70 text-base hover:op100 />
</div>
</div>
</div>
Expand All @@ -200,7 +200,7 @@ onUnmounted(() => {
<div v-if="nodeActions?.length" class="mb-1 flex justify-end pb-1" border="b dashed base">
<div class="flex items-center gap-2 px-1">
<div v-for="(action, index) in nodeActions" :key="index" v-tooltip.bottom-end="{ content: action.tooltip }" class="flex items-center gap1" @click="callNodeAction(index)">
<i :class="`i-ic-baseline-${action.icon.replace(/\_/g, '-')}`" cursor-pointer op70 text-base hover:op100 />
<VueIcIcon :name="`baseline-${action.icon.replace(/\_/g, '-')}`" cursor-pointer op70 text-base hover:op100 />
</div>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion packages/applet/src/modules/custom-inspector/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const routes = computed(() => {
path: '/about',
name: 'About',
component: About,
icon: 'https://raw.githubusercontent.com/TanStack/query/main/packages/vue-query/media/vue-query.svg',
},
].filter(Boolean) as VirtualRoute[]
})
Expand Down
2 changes: 1 addition & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
},
"dependencies": {
"@unocss/preset-icons": "^0.61.0",
"@unocss/runtime": "^0.61.0",
"@vue/devtools-applet": "workspace:^",
"@vue/devtools-core": "workspace:^",
"@vue/devtools-kit": "workspace:^",
Expand All @@ -34,6 +33,7 @@
"@vueuse/core": "^10.11.0",
"@vueuse/integrations": "^10.11.0",
"colord": "^2.9.3",
"fast-deep-equal": "^3.1.3",
"fuse.js": "^7.0.0",
"minimatch": "^9.0.4",
"shiki": "^1.9.0",
Expand Down
1 change: 0 additions & 1 deletion packages/client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Pane, Splitpanes } from 'splitpanes'
import { useDevToolsColorMode } from '@vue/devtools-ui'
import { DevToolsMessagingEvents, onDevToolsConnected, onRpcConnected, refreshCurrentPageData, rpc, useDevToolsState } from '@vue/devtools-core'
import('./setup/unocss-runtime')
useDevToolsColorMode()
const router = useRouter()
const route = useRoute()
Expand Down
8 changes: 5 additions & 3 deletions packages/client/src/components/common/SideNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ const overflowTabs = computed(() => flattenedTabs.value.slice(containerCapacity.
const categorizedInlineTabs = getCategorizedTabs(inlineTabs, enabledTabs)
const categorizedOverflowTabs = getCategorizedTabs(overflowTabs, enabledTabs)
const displayedTabs = computed(() => (sidebarScrollable.value || sidebarExpanded.value)
? enabledTabs.value
: categorizedInlineTabs.value)
const displayedTabs = computed(() =>
(sidebarScrollable.value || sidebarExpanded.value)
? enabledTabs.value
: categorizedInlineTabs.value,
)
onClickOutside(
panel,
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/components/common/SideNavItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function onClick() {
text-xl
:icon="tab.icon"
:fallback="tab.fallbackIcon"
title="Settings"
:title="tab.name"
:show-title="false"
/>
<span v-if="!minimized" text-md overflow-hidden text-ellipsis ws-nowrap>
Expand Down
12 changes: 11 additions & 1 deletion packages/client/src/components/common/TabIcon.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<script setup lang="ts">
import { isUrlString } from '@vue/devtools-shared'
import { VueIcIcon } from '@vue/devtools-ui'
const props = withDefaults(defineProps<{
icon?: string
title?: string
Expand All @@ -13,11 +16,14 @@ const _icon = ref<string | undefined>(props.icon)
function onLoadError() {
_icon.value = props.fallback
}
// For custom-inspector icons, the prefix is 'custom-ic-'
const CUSTOM_IC_ICON_PREFIX = 'custom-ic-'
</script>

<template>
<img
v-if="_icon && (_icon.startsWith('/') || _icon.match(/^https?:/))"
v-if="_icon && isUrlString(_icon)"
:style="{
width: '1em',
height: '1em',
Expand All @@ -27,6 +33,10 @@ function onLoadError() {
:alt="title"
@error="onLoadError"
>
<VueIcIcon
v-else-if="_icon?.startsWith(CUSTOM_IC_ICON_PREFIX)" :name="_icon.slice(CUSTOM_IC_ICON_PREFIX.length)"
v-bind="$attrs" :title="showTitle ? title : undefined"
/>
<div
v-else
:style="{
Expand Down
1 change: 0 additions & 1 deletion packages/client/src/composables/custom-inspector-tabs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useCustomInspector } from '@vue/devtools-applet'
// @ts-expect-error skip type check
import type { CustomInspectorType } from '@vue/devtools-applet'

import type { ModuleBuiltinTab } from '~/types/tab'
Expand Down
19 changes: 12 additions & 7 deletions packages/client/src/composables/state-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { randomStr } from '@vue/devtools-shared'
import { CustomCommand } from '@vue/devtools-kit'
import { MaybeRefOrGetter } from 'vue'
import { useDevToolsState } from '@vue/devtools-core'
import equal from 'fast-deep-equal'

export interface CommandItem {
id: string
Expand All @@ -26,17 +27,21 @@ export function useCommands() {
const router = useRouter()
const state = useDevToolsState()

const customCommands = ref<CustomCommand[]>(state.commands.value || [])
let cachedCustomCommands: CustomCommand[] = []

watchEffect(() => {
customCommands.value = state.commands.value || []
const customCommands = computed(() => {
if (equal(state.commands.value, cachedCustomCommands))
// Optimized computed value
return cachedCustomCommands
cachedCustomCommands = state.commands.value
return state.commands.value
})

const fixedCommands: CommandItem[] = [
{
id: 'fixed:settings',
title: 'Settings',
icon: 'carbon-settings-adjust',
icon: 'i-carbon-settings-adjust',
action: () => {
router.push('/settings')
},
Expand Down Expand Up @@ -129,10 +134,10 @@ export async function getVueDocsCommands() {
if (!_vueDocsCommands) {
const list = await import('../../data/vue-apis.json').then(i => i.default)
_vueDocsCommands = list.map(i => ({
...i,
icon: 'carbon-api-1',
...i!,
icon: 'i-carbon-api-1',
action: () => {
window.open(i.url, '_blank')
window.open(i!.url, '_blank')
},
}))
}
Expand Down
12 changes: 9 additions & 3 deletions packages/client/src/composables/state-tab.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { MaybeRef } from 'vue'
import type { CustomTab } from '@vue/devtools-kit'
import { isInChromePanel, isInElectron } from '@vue/devtools-shared'
import equal from 'fast-deep-equal'
import { useDevToolsState } from '@vue/devtools-core'

import type { ModuleBuiltinTab } from '~/types/tab'
Expand All @@ -26,9 +27,14 @@ export function useAllTabs() {
const state = useDevToolsState()
const customInspectorTabs = useCustomInspectorTabs()

const customTabs = ref<CustomTab[]>(state.tabs.value || [])
watchEffect(() => {
customTabs.value = state.tabs.value
let cachedCustomTabs: CustomTab[] = []

const customTabs = computed(() => {
if (equal(state.tabs.value, cachedCustomTabs))
// Optimized computed value
return cachedCustomTabs
cachedCustomTabs = state.tabs.value
return state.tabs.value
})
const allTabs = computed(() => {
const vitePluginDetected = state.vitePluginDetected.value
Expand Down
19 changes: 0 additions & 19 deletions packages/client/src/setup/unocss-runtime.ts
Original file line number Diff line number Diff line change
@@ -1,19 +0,0 @@
import presetIcons from '@unocss/preset-icons/browser'
import init from '@unocss/runtime'

init({
defaults: {
presets: [
presetIcons({
prefix: ['i-', ''],
collections: {},
scale: 1.2,
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
],
},
bypassDefined: true,
})
2 changes: 1 addition & 1 deletion packages/devtools-kit/src/ctx/inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function getActiveInspectors() {
id: options.id,
label: options.label,
logo: descriptor.logo!,
icon: `i-ic-baseline-${options?.icon?.replace(/_/g, '-')}`,
icon: `custom-ic-baseline-${options?.icon?.replace(/_/g, '-')}`,
packageName: descriptor.packageName,
homepage: descriptor.homepage,
}
Expand Down
32 changes: 29 additions & 3 deletions packages/devtools-kit/src/ctx/state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { target as global } from '@vue/devtools-shared'
import { target as global, isUrlString } from '@vue/devtools-shared'
import { debounce } from 'perfect-debounce'
import type { AppRecord, CustomCommand, CustomTab } from '../types'
import { DevToolsMessagingHookKeys } from './hook'
Expand Down Expand Up @@ -165,12 +165,29 @@ export function onDevToolsConnected(fn: () => void) {
})
}

const resolveIcon = (icon?: string) => {
if (!icon)
return
if (icon.startsWith('baseline-')) {
return `custom-ic-${icon}`
}
// devtools internal custom tab icons are starts with `i-` prefix, render as it is set in unocss safelist
// or if it's a url
if (icon.startsWith('i-') || isUrlString(icon))
return icon
// for custom-tab, we use `custom-ic-` prefix
return `custom-ic-baseline-${icon}`
}

export function addCustomTab(tab: CustomTab) {
const tabs = global.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__
if (tabs.some((t: CustomTab) => t.name === tab.name))
return

tabs.push(tab)
tabs.push({
...tab,
icon: resolveIcon(tab.icon),
})
updateAllStates()
}

Expand All @@ -179,7 +196,16 @@ export function addCustomCommand(action: CustomCommand) {
if (commands.some((t: CustomCommand) => t.id === action.id))
return

commands.push(action)
commands.push({
...action,
icon: resolveIcon(action.icon),
children: action.children
? action.children.map((child: CustomCommand) => ({
...child,
icon: resolveIcon(child.icon),
}))
: undefined,
})
updateAllStates()
}

Expand Down
Loading

0 comments on commit bc97b34

Please sign in to comment.