Skip to content

Commit 3df27b9

Browse files
authored
feat: vue i18n tab (#168)
1 parent 8886a73 commit 3df27b9

File tree

21 files changed

+394
-1
lines changed

21 files changed

+394
-1
lines changed

packages/client/src/constants/tab.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ export const builtinTab: [string, ModuleBuiltinTab[]][] = [
5656
path: 'pinia',
5757
title: 'Pinia',
5858
},
59+
{
60+
icon: 'i-carbon-language',
61+
name: 'i18n',
62+
order: -100,
63+
path: 'i18n',
64+
title: 'I18n Resources',
65+
},
5966
]],
6067
['advanced', [
6168
{
@@ -79,6 +86,7 @@ type Detective = NonNullable<DevtoolsBridgeAppRecord['moduleDetectives']>
7986
const moduleDetectivesMapping = {
8087
pinia: 'pinia',
8188
router: 'vueRouter',
89+
i18n: 'vueI18n',
8290
} satisfies Record<string, keyof Detective>
8391

8492
export function isDetected(moduleDetectives: Detective, tab: ModuleBuiltinTab) {

packages/client/src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Components from '~/pages/components.vue'
1414
import Overview from '~/pages/overview.vue'
1515
import PiniaPage from '~/pages/pinia.vue'
1616
import RouterPage from '~/pages/router.vue'
17+
import I18nPage from '~/pages/i18n.vue'
1718
import Timeline from '~/pages/timeline.vue'
1819
import Pages from '~/pages/pages.vue'
1920
import Assets from '~/pages/assets.vue'
@@ -40,6 +41,7 @@ const routes = [
4041
{ path: '/components', component: Components },
4142
{ path: '/pinia', component: PiniaPage },
4243
{ path: '/router', component: RouterPage },
44+
{ path: '/i18n', component: I18nPage },
4345
{ path: '/timeline', component: Timeline },
4446
{ path: '/pages', component: Pages },
4547
{ path: '/assets', component: Assets },

packages/client/src/pages/i18n.vue

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<script setup lang="ts">
2+
import { useDevToolsBridgeRpc } from '@vue/devtools-core'
3+
4+
// eslint-disable-next-line ts/no-import-type-side-effects
5+
import { type InspectorNodeTag, type InspectorState } from '@vue/devtools-kit'
6+
import { Pane, Splitpanes } from 'splitpanes'
7+
8+
const bridgeRpc = useDevToolsBridgeRpc()
9+
const INSPECTOR_ID = 'vue-i18n-resource-inspector'
10+
11+
const selected = ref('')
12+
const tree = ref<{ id: string, label: string, tags: InspectorNodeTag[] }[]>([])
13+
const state = ref<{
14+
inspectorId?: string
15+
state?: InspectorState[]
16+
getters?: InspectorState[]
17+
}>({})
18+
19+
function getI18nState(nodeId: string) {
20+
bridgeRpc.getInspectorState({ inspectorId: INSPECTOR_ID, nodeId }).then(({ data }) => {
21+
state.value = data
22+
})
23+
}
24+
25+
function clearI18nState() {
26+
state.value = {}
27+
}
28+
29+
watch(selected, () => {
30+
clearI18nState()
31+
getI18nState(selected.value)
32+
})
33+
34+
createCollapseContext('inspector-state')
35+
36+
onDevToolsClientConnected(() => {
37+
bridgeRpc.getInspectorTree({ inspectorId: INSPECTOR_ID, filter: '' }).then(({ data }) => {
38+
tree.value = data
39+
if (!selected.value && data.length) {
40+
selected.value = data[0].id
41+
getI18nState(data[0].id)
42+
}
43+
})
44+
45+
bridgeRpc.on.inspectorTreeUpdated((data) => {
46+
if (!data?.data.length)
47+
return
48+
tree.value = data.data
49+
if (!selected.value && data.data.length) {
50+
selected.value = data.data[0].id
51+
getI18nState(data.data[0].id)
52+
}
53+
}, {
54+
inspectorId: INSPECTOR_ID,
55+
})
56+
57+
bridgeRpc.on.inspectorStateUpdated((data) => {
58+
if (!data || !data.state.length)
59+
return
60+
61+
state.value = data
62+
}, {
63+
inspectorId: INSPECTOR_ID,
64+
})
65+
})
66+
67+
onUnmounted(() => {
68+
bridgeRpc.unhighlightElement()
69+
})
70+
</script>
71+
72+
<template>
73+
<PanelGrids h-screen>
74+
<Splitpanes>
75+
<Pane flex flex-col border="r base">
76+
<div h-screen select-none overflow-scroll p-2 class="no-scrollbar">
77+
<InspectorTree v-model="selected" :data="tree" />
78+
</div>
79+
</Pane>
80+
<Pane flex flex-col>
81+
<div :key="selected" h-0 grow overflow-auto p-2 class="no-scrollbar">
82+
<InspectorState
83+
v-for="(item, key) in state" :id="key"
84+
:key="key"
85+
:inspector-id="INSPECTOR_ID"
86+
:node-id="selected" :data="item" :name="`${key}`"
87+
/>
88+
</div>
89+
</Pane>
90+
</Splitpanes>
91+
</PanelGrids>
92+
</template>

packages/core/src/bridge/core.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export const bridgeRpcEvents = {
8585
toggleApp: 'toggle-app',
8686
isVueInspectorDetected: 'vue-inspector:detected',
8787
enableVueInspector: 'vue-inspector:enable',
88+
unhighlightElement: 'element:unhighlight',
8889
} as const
8990

9091
export type BridgeRpcEvents = typeof bridgeRpcEvents
@@ -116,6 +117,7 @@ export interface BridgeRpcEventPayload {
116117
[bridgeRpcEvents.toggleApp]: string
117118
[bridgeRpcEvents.isVueInspectorDetected]: boolean
118119
[bridgeRpcEvents.enableVueInspector]: null
120+
[bridgeRpcEvents.unhighlightElement]: null
119121
}
120122

121123
export class BridgeRpcCore {

packages/core/src/bridge/devtools.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ export class BridgeRpc {
9595
return devtoolsBridge.rpc.emit<{ data: { id: string } }>(bridgeRpcEvents.inspectComponentInspector)
9696
}
9797

98+
static unhighlightElement() {
99+
return devtoolsBridge.rpc.emit(bridgeRpcEvents.unhighlightElement)
100+
}
101+
98102
static scrollToComponent(payload: BridgeRpcEventPayload['scroll-to-component']) {
99103
return devtoolsBridge.rpc.emit(bridgeRpcEvents.scrollToComponent, payload)
100104
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export function registerBridgeRpc(bridge: BridgeInstanceType) {
4242
return devtools.api.inspectComponentInspector()
4343
})
4444

45+
// unhighlight element
46+
bridgeRpcCore.on(bridgeRpcEvents.unhighlightElement, () => {
47+
devtools.api.unhighlightElement()
48+
return Promise.resolve(JSON.stringify({}))
49+
})
50+
4551
// scroll to component
4652
bridgeRpcCore.on(bridgeRpcEvents.scrollToComponent, (payload) => {
4753
devtools.api.scrollToComponent(payload!)

packages/devtools-kit/src/api/index.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { addCustomCommand, removeCustomCommand } from '../core/custom-command'
1414
import type { CustomCommand } from '../core/custom-command'
1515

1616
import { getVueInspector } from '../core/vue-inspector'
17-
import { inspectComponentInspector, scrollToComponent, toggleComponentInspector } from '../core/component-inspector'
17+
import { highlight as highlightElement, inspectComponentInspector, scrollToComponent, toggleComponentInspector, unhighlight as unhighlightElement } from '../core/component-inspector'
1818
import { clear } from './off'
1919
import type { DevToolsEvent } from './on'
2020
import { DevToolsEvents, apiHooks, on } from './on'
@@ -178,6 +178,23 @@ export class DevToolsPluginApi {
178178
openInEditor(payload)
179179
}
180180

181+
highlightElement(instance) {
182+
highlightElement(instance)
183+
}
184+
185+
unhighlightElement() {
186+
unhighlightElement()
187+
}
188+
189+
async getComponentInstances(app) {
190+
const appRecord = app.__VUE_DEVTOOLS_APP_RECORD__
191+
const appId = appRecord.id.toString()
192+
const instances = [...appRecord.instanceMap]
193+
.filter(([key]) => key.split(':')[0] === appId)
194+
.map(([,instance]) => instance)
195+
return instances
196+
}
197+
181198
// Vite only
182199
getVueInspector() {
183200
return getVueInspector()

packages/devtools-kit/src/api/plugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ export async function registerPlugin(options: { app: VueAppInstance, api: DevToo
4141
const globalProperties = record.app?.config?.globalProperties
4242
if (!globalProperties)
4343
return record
44+
4445
return {
4546
...record,
4647
moduleDetectives: {
4748
vueRouter: !!globalProperties.$router,
4849
pinia: !!globalProperties.$pinia,
50+
vueI18n: !!globalProperties.$i18n,
4951
},
5052
}
5153
})

packages/devtools-kit/src/core/component-inspector/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,19 @@ export function toggleComponentInspector(options: ToggleComponentInspectorOption
142142
}
143143
}
144144

145+
export function highlight(instance: VueAppInstance) {
146+
const bounds = getComponentBoundingRect(instance)
147+
const name = getInstanceName(instance)
148+
const container = getCotainerElement()
149+
container ? update({ bounds, name }) : create({ bounds, name })
150+
}
151+
152+
export function unhighlight() {
153+
const el = getCotainerElement()
154+
if (el)
155+
el.style.display = 'none'
156+
}
157+
145158
let inspectInstance: VueAppInstance = null!
146159
function inspectFn(e: MouseEvent) {
147160
const target = e.target as { __vueParentComponent?: VueAppInstance }

packages/playground/locales/en.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
button:
2+
about: About
3+
back: Back
4+
go: GO
5+
home: Home
6+
toggle_dark: Toggle dark mode
7+
toggle_langs: Change languages
8+
intro:
9+
desc: Opinionated Vite Starter Template
10+
dynamic-route: Demo of dynamic route
11+
hi: Hi, {name}!
12+
aka: Also known as
13+
whats-your-name: What's your name?
14+
not-found: Not found

0 commit comments

Comments
 (0)