Skip to content

Commit 6359b3e

Browse files
authored
feat: new components/pinia/router panel (#345)
1 parent 4e1777f commit 6359b3e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1351
-783
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"stub:devtools-kit": "turbo stub --filter=./packages/devtools-kit",
2929
"stub:overlay": "turbo stub --filter=./packages/overlay",
3030
"stub:client": "turbo stub --filter=./packages/client",
31+
"stub:applet": "turbo stub --filter=./packages/applet",
3132
"stub:vite": "turbo stub --filter=./packages/vite",
3233
"stub:devtools-api": "turbo stub --filter=./packages/devtools-api...",
3334
"build:shared": "turbo build --filter=./packages/shared...",

packages/applet/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@
2828
"@vue/devtools-kit": "workspace:^",
2929
"@vue/devtools-shared": "workspace:^",
3030
"@vue/devtools-ui": "workspace:^",
31+
"lodash-es": "^4.17.21",
3132
"perfect-debounce": "^1.0.0",
33+
"shiki": "1.3.0",
3234
"splitpanes": "^3.1.5",
3335
"vue-virtual-scroller": "2.0.0-beta.8"
3436
},
3537
"devDependencies": {
38+
"@types/lodash-es": "^4.17.12",
3639
"unplugin-vue": "^5.0.5",
3740
"vite-plugin-dts": "^3.8.3",
3841
"vue": "^3.4.23",
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<script setup lang="ts">
2+
// This components requires to run in DevTools to render correctly
3+
import { computed, nextTick } from 'vue'
4+
import type { BuiltinLanguage } from 'shiki'
5+
import { renderCodeHighlight } from '~/composables/shiki'
6+
7+
const props = withDefaults(
8+
defineProps<{
9+
code: string
10+
lang?: BuiltinLanguage | 'text'
11+
lines?: boolean
12+
transformRendered?: (code: string) => string
13+
}>(),
14+
{
15+
lines: true,
16+
},
17+
)
18+
19+
const emit = defineEmits(['loaded'])
20+
21+
const rendered = computed(() => {
22+
const result = props.lang === 'text'
23+
? { code: props.code, supported: false }
24+
: renderCodeHighlight(props.code, props.lang) || { code: props.code, supported: false }
25+
if (result.supported && props.transformRendered)
26+
result.code = props.transformRendered(result.code)
27+
if (result.supported)
28+
nextTick(() => emit('loaded'))
29+
return result
30+
})
31+
</script>
32+
33+
<template>
34+
<template v-if="lang && rendered.supported">
35+
<pre
36+
class="code-block"
37+
:class="lines ? 'code-block-lines' : ''"
38+
v-html="rendered.code"
39+
/>
40+
</template>
41+
<template v-else>
42+
<pre
43+
class="code-block"
44+
:class="lines ? 'code-block-lines' : ''"
45+
><pre class="shiki"><code><template v-for="line, _idx in code.split('\n')" :key="_idx"><span class="line" v-text="line" /><br></template></code></pre></pre>
46+
</template>
47+
</template>
48+
49+
<style>
50+
.code-block-lines .shiki code {
51+
counter-reset: step;
52+
counter-increment: step calc(var(--start, 1) - 1);
53+
}
54+
.code-block-lines .shiki code .line::before {
55+
content: counter(step);
56+
counter-increment: step;
57+
width: 2.5rem;
58+
padding-right: 0.5rem;
59+
margin-right: 0.5rem;
60+
display: inline-block;
61+
text-align: right;
62+
--at-apply: 'text-truegray:50';
63+
}
64+
</style>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<script setup lang="ts">
2+
import type { InspectorNodeTag } from '@vue/devtools-kit'
3+
import { VTooltip as vTooltip } from '@vue/devtools-ui'
4+
import { toHex } from '~/utils'
5+
6+
defineProps<{
7+
tag: InspectorNodeTag
8+
}>()
9+
</script>
10+
11+
<template>
12+
<span
13+
v-tooltip="{
14+
content: tag.tooltip,
15+
html: true,
16+
}"
17+
:style="{
18+
color: `#${toHex(tag.textColor)}`,
19+
backgroundColor: `#${toHex(tag.backgroundColor)}`,
20+
}"
21+
class="ml-2 rounded-sm px-1 text-[0.75rem] leading-snug"
22+
>
23+
{{ tag.label }}
24+
</span>
25+
</template>

packages/applet/src/components/basic/SelectiveList.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<script setup lang="ts">
22
import { defineModel } from 'vue'
3+
import type { InspectorNodeTag } from '@vue/devtools-kit'
4+
import NodeTag from '~/components/basic/NodeTag.vue'
35
4-
defineProps<{ data: { id: string, label: string }[] }>()
6+
defineProps<{ data: { id: string, label: string, tags: InspectorNodeTag[] }[] }>()
57
68
const selected = defineModel()
79
@@ -19,6 +21,7 @@ function select(id: string) {
1921
@click="select(item.id)"
2022
>
2123
{{ item.label }}
24+
<NodeTag v-for="(_item, index) in item.tags" :key="index" :tag="_item" />
2225
</li>
2326
</ul>
2427
</template>

packages/applet/src/components/state/RootStateViewer.vue

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ const props = withDefaults(defineProps<{
1111
nodeId: string
1212
inspectorId: string
1313
disableEdit?: boolean
14+
expandedStateId?: string
1415
}>(), {
1516
disableEdit: false,
17+
expandedStateId: '',
1618
})
1719
1820
function initEditorContext() {
@@ -28,15 +30,7 @@ watchEffect(() => {
2830
context.value = initEditorContext()
2931
})
3032
31-
const { expanded, toggleExpanded } = useToggleExpanded()
32-
33-
watchEffect(() => {
34-
// Expand the root level by default
35-
Object.keys(props.data).forEach((_, index) => {
36-
if (!expanded.value.includes(`${index}`))
37-
toggleExpanded(`${index}`)
38-
})
39-
})
33+
const { expanded, toggleExpanded } = useToggleExpanded(props.expandedStateId)
4034
</script>
4135

4236
<template>

packages/applet/src/components/state/StateFieldEditor.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function quickEditNum(v: number | string, offset: 1 | -1) {
6868
</script>
6969

7070
<template>
71-
<div class="inline pl5px">
71+
<div class="flex pl5px">
7272
<!-- only editable will show operate actions -->
7373
<template v-if="!props.disableEdit && data.editable">
7474
<!-- input edit, number/string/object -->

packages/applet/src/components/state/StateFieldViewer.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const normalizedDisplayedKey = computed(() => {
5858
// normalized display value
5959
const normalizedDisplayedValue = computed(() => {
6060
const directlyDisplayedValueMap = ['Reactive']
61-
const extraDisplayedValue = (props.data as InspectorCustomState)._custom?.stateTypeName || props.data?.stateTypeName
61+
const extraDisplayedValue = (props.data as InspectorCustomState)?._custom?.stateTypeName || props.data?.stateTypeName
6262
if (directlyDisplayedValueMap.includes(extraDisplayedValue!)) {
6363
return extraDisplayedValue
6464
}
@@ -68,10 +68,10 @@ const normalizedDisplayedValue = computed(() => {
6868
}
6969
7070
else {
71-
const _type = (props.data.value as InspectorCustomState)._custom?.type
71+
const _type = (props.data.value as InspectorCustomState)?._custom?.type
7272
const _value = type.value === 'custom' && !_type ? `"${displayedValue.value}"` : (displayedValue.value === '' ? `""` : displayedValue.value)
7373
const normalizedType = type.value === 'custom' && _type === 'ref' ? getInspectorStateValueType(_value) : type.value
74-
const result = `<span class="${normalizedType}-state-type">${_value}</span>`
74+
const result = `<span class="${normalizedType}-state-type flex whitespace-nowrap">${_value}</span>`
7575
7676
if (extraDisplayedValue)
7777
return `${result} <span class="text-gray-500">(${extraDisplayedValue})</span>`
@@ -197,13 +197,13 @@ function submitDrafting() {
197197
/>
198198
<!-- placeholder -->
199199
<span v-else pl5 />
200-
<span op70>
200+
<span op70 class="whitespace-nowrap">
201201
{{ normalizedDisplayedKey }}
202202
</span>
203203
<span mx1>:</span>
204204
<StateFieldInputEditor v-if="editing" v-model="editingText" :custom-type="raw.customType" @cancel="toggleEditing" @submit="submit" />
205-
<span :class="stateFormatClass">
206-
<span v-html="normalizedDisplayedValue" />
205+
<span :class="stateFormatClass" class="flex whitespace-nowrap">
206+
<span class="flex" v-html="normalizedDisplayedValue" />
207207
</span>
208208
<StateFieldEditor
209209
:hovering="isHovering" :disable-edit="state.disableEdit"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<script setup lang="ts">
2+
import type { ComponentTreeNode } from '@vue/devtools-kit'
3+
import ToggleExpanded from '~/components/basic/ToggleExpanded.vue'
4+
import ComponentTreeViewer from '~/components/tree/TreeViewer.vue'
5+
import NodeTag from '~/components/basic/NodeTag.vue'
6+
7+
import { useToggleExpanded } from '~/composables/toggle-expanded'
8+
import { useSelect } from '~/composables/select'
9+
10+
withDefaults(defineProps<{
11+
data: ComponentTreeNode[]
12+
depth: number
13+
}>(), {
14+
depth: 0,
15+
})
16+
const selectedNodeId = defineModel()
17+
const { expanded, toggleExpanded } = useToggleExpanded()
18+
const { select: _select } = useSelect()
19+
20+
function select(id: string) {
21+
selectedNodeId.value = id
22+
}
23+
</script>
24+
25+
<template>
26+
<div
27+
v-for="(item, index) in data"
28+
:key="index"
29+
>
30+
<div
31+
class="group flex cursor-pointer items-center rounded-1 hover:(bg-primary-300 dark:bg-gray-600)"
32+
:style=" { paddingLeft: `${15 * depth + 4}px` }"
33+
:class="{ 'bg-primary-600! active': selectedNodeId === item.id }"
34+
@click="select(item.id)"
35+
>
36+
<ToggleExpanded
37+
v-if="item?.children?.length"
38+
:value="expanded.includes(item.id)"
39+
class="[.active_&]:op20 group-hover:op20"
40+
@click.stop="toggleExpanded(item.id)"
41+
/>
42+
<!-- placeholder -->
43+
<span v-else pl5 />
44+
<span font-state-field text-4>
45+
<span class="text-gray-400 dark:text-gray-600 group-hover:(text-white op50) [.active_&]:(op50 text-white!)">&lt;</span>
46+
<span group-hover:text-white class="[.active_&]:(text-white)">{{ item.name }}</span>
47+
<span class="text-gray-400 dark:text-gray-600 group-hover:(text-white op50) [.active_&]:(op50 text-white!)">&gt;</span>
48+
</span>
49+
<NodeTag v-for="(_item, index) in item.tags" :key="index" :tag="_item" />
50+
</div>
51+
<div
52+
v-if="item?.children?.length && expanded.includes(item.id)"
53+
>
54+
<ComponentTreeViewer v-model="selectedNodeId" :data="item?.children" :depth="depth + 1" />
55+
</div>
56+
</div>
57+
</template>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { getDevToolsState, onDevToolsStateUpdated } from '@vue/devtools-core'
2+
import type { InjectionKey, Ref } from 'vue'
3+
import { inject, provide, ref } from 'vue'
4+
5+
import type { AppRecord } from '@vue/devtools-kit'
6+
7+
export type DevtoolsBridgeAppRecord = Pick<AppRecord, 'name' | 'id' | 'version' | 'routerId' | 'moduleDetectives'>
8+
9+
const VueDevToolsStateSymbol: InjectionKey<{
10+
appRecords: Ref<Array<DevtoolsBridgeAppRecord>>
11+
activeAppRecordId: Ref<string>
12+
}> = Symbol('VueDevToolsStateSymbol')
13+
14+
export function createDevToolsStateContext() {
15+
const appRecords = ref<Array<DevtoolsBridgeAppRecord>>([])
16+
const activeAppRecordId = ref('')
17+
18+
getDevToolsState().then((data) => {
19+
appRecords.value = data!.appRecords
20+
activeAppRecordId.value = data!.activeAppRecordId!
21+
})
22+
23+
onDevToolsStateUpdated((data) => {
24+
appRecords.value = data.appRecords
25+
activeAppRecordId.value = data.activeAppRecordId!
26+
})
27+
28+
provide(VueDevToolsStateSymbol, {
29+
appRecords,
30+
activeAppRecordId,
31+
})
32+
33+
return {
34+
appRecords,
35+
activeAppRecordId,
36+
}
37+
}
38+
39+
export function useDevToolsState() {
40+
return inject(VueDevToolsStateSymbol, {
41+
appRecords: ref([]),
42+
activeAppRecordId: ref(''),
43+
})
44+
}

0 commit comments

Comments
 (0)