Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(client): add color picker and inspector commands #162

Merged
merged 2 commits into from
Jan 8, 2024
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
37 changes: 36 additions & 1 deletion packages/client/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import { useDevToolsBridge, useDevToolsState } from '@vue/devtools-core'
import { useDevToolsBridge, useDevToolsBridgeRpc, useDevToolsState } from '@vue/devtools-core'
import { isInChromePanel } from '@vue/devtools-shared'
import { Pane, Splitpanes } from 'splitpanes'

Expand All @@ -11,6 +11,7 @@ useColorMode()
const router = useRouter()
const route = useRoute()
const { connected, clientConnected } = useDevToolsState()
const bridgeRpc = useDevToolsBridgeRpc()
const clientState = devtoolsClientState

const viewMode = inject<Ref<'overlay' | 'panel'>>('viewMode', ref('overlay'))
Expand Down Expand Up @@ -66,6 +67,40 @@ watchEffect(() => {
activeAppRecords.value = devtoolsState.appRecords.value
activeAppRecordId.value = devtoolsState.activeAppRecordId.value
})

// register commands
const { copy } = useCopy()
const eyeDropper = useEyeDropper({})

bridgeRpc?.isVueInspectorDetected?.()?.then(({ data }) => {
if (data) {
registerCommands(() =>
[{
id: 'action:vue-inspector',
title: 'Inspector',
icon: 'i-carbon-select-window',
action: async () => {
bridge.value.emit('toggle-panel', false)
await bridgeRpc.enableVueInspector()
},
}],
)
}
})
registerCommands(() => [
...(eyeDropper.isSupported.value
? [{
id: 'action:eye-dropper',
title: 'Color Picker',
icon: 'i-carbon-eyedropper',
action: async () => {
const { sRGBHex } = await eyeDropper.open() || {}
if (sRGBHex)
copy(sRGBHex)
},
}]
: []),
])
</script>

<template>
Expand Down
10 changes: 8 additions & 2 deletions packages/client/src/composables/state-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export interface CommandItem {
action: () => void | CommandItem[] | Promise<CommandItem[] | void>
}

function uniqueById(items: CommandItem[]): CommandItem[] {
const unique = new Map<number | string, CommandItem>()
items.forEach(item => unique.set(item.id, item))
return Array.from(unique.values())
}

const registeredCommands = reactive(new Map<string, MaybeRefOrGetter<CommandItem[]>>())

// @unocss-include
Expand Down Expand Up @@ -68,13 +74,13 @@ export function useCommands() {
}))

return computed(() => {
return [
return uniqueById([
...fixedCommands,
...tabCommands.value,
...resolveCustomCommands(customCommands.value),
...Array.from(registeredCommands.values())
.flatMap(i => toValue(i)),
]
])
})
}

Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/bridge/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ export const bridgeRpcEvents = {
editState: 'edit-inspector-state',
openInEditor: 'open-in-editor',
toggleApp: 'toggle-app',
isVueInspectorDetected: 'vue-inspector:detected',
enableVueInspector: 'vue-inspector:enable',
} as const

export type BridgeRpcEvents = typeof bridgeRpcEvents
Expand Down Expand Up @@ -112,6 +114,8 @@ export interface BridgeRpcEventPayload {
[bridgeRpcEvents.editState]: InspectorStateEditorPayload
[bridgeRpcEvents.openInEditor]: string
[bridgeRpcEvents.toggleApp]: string
[bridgeRpcEvents.isVueInspectorDetected]: boolean
[bridgeRpcEvents.enableVueInspector]: null
}

export class BridgeRpcCore {
Expand All @@ -122,7 +126,7 @@ export class BridgeRpcCore {

on<E extends BridgeRpcEventName>(
eventName: E,
handler: (payload?: BridgeRpcEventPayload[E]) => Promise<string | void> | string,
handler: (payload?: BridgeRpcEventPayload[E]) => Promise<boolean | string | void> | string,
) {
this.bridge.on(`${eventName}:req`, async (payload?: BridgeRpcEventPayload[E]) => {
const res = await handler(payload)
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/bridge/devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ export class BridgeRpc {
return devtoolsBridge.rpc.emit<void>(bridgeRpcEvents.openInEditor, JSON.stringify(payload))
}

static async isVueInspectorDetected() {
return devtoolsBridge.rpc.emit<{ data: boolean }>(bridgeRpcEvents.isVueInspectorDetected)
}

static async enableVueInspector() {
return devtoolsBridge.rpc.emit<void>(bridgeRpcEvents.enableVueInspector)
}

static async toggleApp(id: string) {
return devtoolsBridge.rpc.emit<void>(bridgeRpcEvents.toggleApp, id)
}
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/bridge/user-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ export function registerBridgeRpc(bridge: BridgeInstanceType) {
return devtools.api.openInEditor(JSON.parse(payload!))
})

// get vue inspector
bridgeRpcCore.on(bridgeRpcEvents.isVueInspectorDetected, async () => {
return !!await devtools.api.getVueInspector()
})

// enable vue inspector
bridgeRpcCore.on(bridgeRpcEvents.enableVueInspector, async () => {
const inspector = await devtools.api.getVueInspector()
if (inspector)
await inspector.enable()
})

// route matched
bridgeRpcCore.on(bridgeRpcEvents.routeMatched, (payload) => {
const c = console.warn
Expand Down