Skip to content

Commit f96ec04

Browse files
authored
feat(client): add color picker and inspector commands (#162)
1 parent ed84e63 commit f96ec04

File tree

5 files changed

+69
-4
lines changed

5 files changed

+69
-4
lines changed

packages/client/src/App.vue

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import type { Ref } from 'vue'
3-
import { useDevToolsBridge, useDevToolsState } from '@vue/devtools-core'
3+
import { useDevToolsBridge, useDevToolsBridgeRpc, useDevToolsState } from '@vue/devtools-core'
44
import { isInChromePanel } from '@vue/devtools-shared'
55
import { Pane, Splitpanes } from 'splitpanes'
66
@@ -11,6 +11,7 @@ useColorMode()
1111
const router = useRouter()
1212
const route = useRoute()
1313
const { connected, clientConnected } = useDevToolsState()
14+
const bridgeRpc = useDevToolsBridgeRpc()
1415
const clientState = devtoolsClientState
1516
1617
const viewMode = inject<Ref<'overlay' | 'panel'>>('viewMode', ref('overlay'))
@@ -66,6 +67,40 @@ watchEffect(() => {
6667
activeAppRecords.value = devtoolsState.appRecords.value
6768
activeAppRecordId.value = devtoolsState.activeAppRecordId.value
6869
})
70+
71+
// register commands
72+
const { copy } = useCopy()
73+
const eyeDropper = useEyeDropper({})
74+
75+
bridgeRpc?.isVueInspectorDetected?.()?.then(({ data }) => {
76+
if (data) {
77+
registerCommands(() =>
78+
[{
79+
id: 'action:vue-inspector',
80+
title: 'Inspector',
81+
icon: 'i-carbon-select-window',
82+
action: async () => {
83+
bridge.value.emit('toggle-panel', false)
84+
await bridgeRpc.enableVueInspector()
85+
},
86+
}],
87+
)
88+
}
89+
})
90+
registerCommands(() => [
91+
...(eyeDropper.isSupported.value
92+
? [{
93+
id: 'action:eye-dropper',
94+
title: 'Color Picker',
95+
icon: 'i-carbon-eyedropper',
96+
action: async () => {
97+
const { sRGBHex } = await eyeDropper.open() || {}
98+
if (sRGBHex)
99+
copy(sRGBHex)
100+
},
101+
}]
102+
: []),
103+
])
69104
</script>
70105

71106
<template>

packages/client/src/composables/state-commands.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ export interface CommandItem {
1212
action: () => void | CommandItem[] | Promise<CommandItem[] | void>
1313
}
1414

15+
function uniqueById(items: CommandItem[]): CommandItem[] {
16+
const unique = new Map<number | string, CommandItem>()
17+
items.forEach(item => unique.set(item.id, item))
18+
return Array.from(unique.values())
19+
}
20+
1521
const registeredCommands = reactive(new Map<string, MaybeRefOrGetter<CommandItem[]>>())
1622

1723
// @unocss-include
@@ -68,13 +74,13 @@ export function useCommands() {
6874
}))
6975

7076
return computed(() => {
71-
return [
77+
return uniqueById([
7278
...fixedCommands,
7379
...tabCommands.value,
7480
...resolveCustomCommands(customCommands.value),
7581
...Array.from(registeredCommands.values())
7682
.flatMap(i => toValue(i)),
77-
]
83+
])
7884
})
7985
}
8086

packages/core/src/bridge/core.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ export const bridgeRpcEvents = {
8383
editState: 'edit-inspector-state',
8484
openInEditor: 'open-in-editor',
8585
toggleApp: 'toggle-app',
86+
isVueInspectorDetected: 'vue-inspector:detected',
87+
enableVueInspector: 'vue-inspector:enable',
8688
} as const
8789

8890
export type BridgeRpcEvents = typeof bridgeRpcEvents
@@ -112,6 +114,8 @@ export interface BridgeRpcEventPayload {
112114
[bridgeRpcEvents.editState]: InspectorStateEditorPayload
113115
[bridgeRpcEvents.openInEditor]: string
114116
[bridgeRpcEvents.toggleApp]: string
117+
[bridgeRpcEvents.isVueInspectorDetected]: boolean
118+
[bridgeRpcEvents.enableVueInspector]: null
115119
}
116120

117121
export class BridgeRpcCore {
@@ -122,7 +126,7 @@ export class BridgeRpcCore {
122126

123127
on<E extends BridgeRpcEventName>(
124128
eventName: E,
125-
handler: (payload?: BridgeRpcEventPayload[E]) => Promise<string | void> | string,
129+
handler: (payload?: BridgeRpcEventPayload[E]) => Promise<boolean | string | void> | string,
126130
) {
127131
this.bridge.on(`${eventName}:req`, async (payload?: BridgeRpcEventPayload[E]) => {
128132
const res = await handler(payload)

packages/core/src/bridge/devtools.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ export class BridgeRpc {
139139
return devtoolsBridge.rpc.emit<void>(bridgeRpcEvents.openInEditor, JSON.stringify(payload))
140140
}
141141

142+
static async isVueInspectorDetected() {
143+
return devtoolsBridge.rpc.emit<{ data: boolean }>(bridgeRpcEvents.isVueInspectorDetected)
144+
}
145+
146+
static async enableVueInspector() {
147+
return devtoolsBridge.rpc.emit<void>(bridgeRpcEvents.enableVueInspector)
148+
}
149+
142150
static async toggleApp(id: string) {
143151
return devtoolsBridge.rpc.emit<void>(bridgeRpcEvents.toggleApp, id)
144152
}

packages/core/src/bridge/user-app.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ export function registerBridgeRpc(bridge: BridgeInstanceType) {
8989
return devtools.api.openInEditor(JSON.parse(payload!))
9090
})
9191

92+
// get vue inspector
93+
bridgeRpcCore.on(bridgeRpcEvents.isVueInspectorDetected, async () => {
94+
return !!await devtools.api.getVueInspector()
95+
})
96+
97+
// enable vue inspector
98+
bridgeRpcCore.on(bridgeRpcEvents.enableVueInspector, async () => {
99+
const inspector = await devtools.api.getVueInspector()
100+
if (inspector)
101+
await inspector.enable()
102+
})
103+
92104
// route matched
93105
bridgeRpcCore.on(bridgeRpcEvents.routeMatched, (payload) => {
94106
const c = console.warn

0 commit comments

Comments
 (0)