Skip to content

Commit

Permalink
feat: new components/pinia/router panel (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
webfansplz committed Apr 23, 2024
1 parent 4e1777f commit 6359b3e
Show file tree
Hide file tree
Showing 73 changed files with 1,351 additions and 783 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -28,6 +28,7 @@
"stub:devtools-kit": "turbo stub --filter=./packages/devtools-kit",
"stub:overlay": "turbo stub --filter=./packages/overlay",
"stub:client": "turbo stub --filter=./packages/client",
"stub:applet": "turbo stub --filter=./packages/applet",
"stub:vite": "turbo stub --filter=./packages/vite",
"stub:devtools-api": "turbo stub --filter=./packages/devtools-api...",
"build:shared": "turbo build --filter=./packages/shared...",
Expand Down
3 changes: 3 additions & 0 deletions packages/applet/package.json
Expand Up @@ -28,11 +28,14 @@
"@vue/devtools-kit": "workspace:^",
"@vue/devtools-shared": "workspace:^",
"@vue/devtools-ui": "workspace:^",
"lodash-es": "^4.17.21",
"perfect-debounce": "^1.0.0",
"shiki": "1.3.0",
"splitpanes": "^3.1.5",
"vue-virtual-scroller": "2.0.0-beta.8"
},
"devDependencies": {
"@types/lodash-es": "^4.17.12",
"unplugin-vue": "^5.0.5",
"vite-plugin-dts": "^3.8.3",
"vue": "^3.4.23",
Expand Down
64 changes: 64 additions & 0 deletions packages/applet/src/components/basic/CodeBlock.vue
@@ -0,0 +1,64 @@
<script setup lang="ts">
// This components requires to run in DevTools to render correctly
import { computed, nextTick } from 'vue'
import type { BuiltinLanguage } from 'shiki'
import { renderCodeHighlight } from '~/composables/shiki'
const props = withDefaults(
defineProps<{
code: string
lang?: BuiltinLanguage | 'text'
lines?: boolean
transformRendered?: (code: string) => string
}>(),
{
lines: true,
},
)
const emit = defineEmits(['loaded'])
const rendered = computed(() => {
const result = props.lang === 'text'
? { code: props.code, supported: false }
: renderCodeHighlight(props.code, props.lang) || { code: props.code, supported: false }
if (result.supported && props.transformRendered)
result.code = props.transformRendered(result.code)
if (result.supported)
nextTick(() => emit('loaded'))
return result
})
</script>

<template>
<template v-if="lang && rendered.supported">
<pre
class="code-block"
:class="lines ? 'code-block-lines' : ''"
v-html="rendered.code"
/>
</template>
<template v-else>
<pre
class="code-block"
:class="lines ? 'code-block-lines' : ''"
><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>
</template>
</template>

<style>
.code-block-lines .shiki code {
counter-reset: step;
counter-increment: step calc(var(--start, 1) - 1);
}
.code-block-lines .shiki code .line::before {
content: counter(step);
counter-increment: step;
width: 2.5rem;
padding-right: 0.5rem;
margin-right: 0.5rem;
display: inline-block;
text-align: right;
--at-apply: 'text-truegray:50';
}
</style>
25 changes: 25 additions & 0 deletions packages/applet/src/components/basic/NodeTag.vue
@@ -0,0 +1,25 @@
<script setup lang="ts">
import type { InspectorNodeTag } from '@vue/devtools-kit'
import { VTooltip as vTooltip } from '@vue/devtools-ui'
import { toHex } from '~/utils'
defineProps<{
tag: InspectorNodeTag
}>()
</script>

<template>
<span
v-tooltip="{
content: tag.tooltip,
html: true,
}"
:style="{
color: `#${toHex(tag.textColor)}`,
backgroundColor: `#${toHex(tag.backgroundColor)}`,
}"
class="ml-2 rounded-sm px-1 text-[0.75rem] leading-snug"
>
{{ tag.label }}
</span>
</template>
5 changes: 4 additions & 1 deletion packages/applet/src/components/basic/SelectiveList.vue
@@ -1,7 +1,9 @@
<script setup lang="ts">
import { defineModel } from 'vue'
import type { InspectorNodeTag } from '@vue/devtools-kit'
import NodeTag from '~/components/basic/NodeTag.vue'
defineProps<{ data: { id: string, label: string }[] }>()
defineProps<{ data: { id: string, label: string, tags: InspectorNodeTag[] }[] }>()
const selected = defineModel()
Expand All @@ -19,6 +21,7 @@ function select(id: string) {
@click="select(item.id)"
>
{{ item.label }}
<NodeTag v-for="(_item, index) in item.tags" :key="index" :tag="_item" />
</li>
</ul>
</template>
12 changes: 3 additions & 9 deletions packages/applet/src/components/state/RootStateViewer.vue
Expand Up @@ -11,8 +11,10 @@ const props = withDefaults(defineProps<{
nodeId: string
inspectorId: string
disableEdit?: boolean
expandedStateId?: string
}>(), {
disableEdit: false,
expandedStateId: '',
})
function initEditorContext() {
Expand All @@ -28,15 +30,7 @@ watchEffect(() => {
context.value = initEditorContext()
})
const { expanded, toggleExpanded } = useToggleExpanded()
watchEffect(() => {
// Expand the root level by default
Object.keys(props.data).forEach((_, index) => {
if (!expanded.value.includes(`${index}`))
toggleExpanded(`${index}`)
})
})
const { expanded, toggleExpanded } = useToggleExpanded(props.expandedStateId)
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion packages/applet/src/components/state/StateFieldEditor.vue
Expand Up @@ -68,7 +68,7 @@ function quickEditNum(v: number | string, offset: 1 | -1) {
</script>

<template>
<div class="inline pl5px">
<div class="flex pl5px">
<!-- only editable will show operate actions -->
<template v-if="!props.disableEdit && data.editable">
<!-- input edit, number/string/object -->
Expand Down
12 changes: 6 additions & 6 deletions packages/applet/src/components/state/StateFieldViewer.vue
Expand Up @@ -58,7 +58,7 @@ const normalizedDisplayedKey = computed(() => {
// normalized display value
const normalizedDisplayedValue = computed(() => {
const directlyDisplayedValueMap = ['Reactive']
const extraDisplayedValue = (props.data as InspectorCustomState)._custom?.stateTypeName || props.data?.stateTypeName
const extraDisplayedValue = (props.data as InspectorCustomState)?._custom?.stateTypeName || props.data?.stateTypeName
if (directlyDisplayedValueMap.includes(extraDisplayedValue!)) {
return extraDisplayedValue
}
Expand All @@ -68,10 +68,10 @@ const normalizedDisplayedValue = computed(() => {
}
else {
const _type = (props.data.value as InspectorCustomState)._custom?.type
const _type = (props.data.value as InspectorCustomState)?._custom?.type
const _value = type.value === 'custom' && !_type ? `"${displayedValue.value}"` : (displayedValue.value === '' ? `""` : displayedValue.value)
const normalizedType = type.value === 'custom' && _type === 'ref' ? getInspectorStateValueType(_value) : type.value
const result = `<span class="${normalizedType}-state-type">${_value}</span>`
const result = `<span class="${normalizedType}-state-type flex whitespace-nowrap">${_value}</span>`
if (extraDisplayedValue)
return `${result} <span class="text-gray-500">(${extraDisplayedValue})</span>`
Expand Down Expand Up @@ -197,13 +197,13 @@ function submitDrafting() {
/>
<!-- placeholder -->
<span v-else pl5 />
<span op70>
<span op70 class="whitespace-nowrap">
{{ normalizedDisplayedKey }}
</span>
<span mx1>:</span>
<StateFieldInputEditor v-if="editing" v-model="editingText" :custom-type="raw.customType" @cancel="toggleEditing" @submit="submit" />
<span :class="stateFormatClass">
<span v-html="normalizedDisplayedValue" />
<span :class="stateFormatClass" class="flex whitespace-nowrap">
<span class="flex" v-html="normalizedDisplayedValue" />
</span>
<StateFieldEditor
:hovering="isHovering" :disable-edit="state.disableEdit"
Expand Down
57 changes: 57 additions & 0 deletions packages/applet/src/components/tree/TreeViewer.vue
@@ -0,0 +1,57 @@
<script setup lang="ts">
import type { ComponentTreeNode } from '@vue/devtools-kit'
import ToggleExpanded from '~/components/basic/ToggleExpanded.vue'
import ComponentTreeViewer from '~/components/tree/TreeViewer.vue'
import NodeTag from '~/components/basic/NodeTag.vue'
import { useToggleExpanded } from '~/composables/toggle-expanded'
import { useSelect } from '~/composables/select'
withDefaults(defineProps<{
data: ComponentTreeNode[]
depth: number
}>(), {
depth: 0,
})
const selectedNodeId = defineModel()
const { expanded, toggleExpanded } = useToggleExpanded()
const { select: _select } = useSelect()
function select(id: string) {
selectedNodeId.value = id
}
</script>

<template>
<div
v-for="(item, index) in data"
:key="index"
>
<div
class="group flex cursor-pointer items-center rounded-1 hover:(bg-primary-300 dark:bg-gray-600)"
:style=" { paddingLeft: `${15 * depth + 4}px` }"
:class="{ 'bg-primary-600! active': selectedNodeId === item.id }"
@click="select(item.id)"
>
<ToggleExpanded
v-if="item?.children?.length"
:value="expanded.includes(item.id)"
class="[.active_&]:op20 group-hover:op20"
@click.stop="toggleExpanded(item.id)"
/>
<!-- placeholder -->
<span v-else pl5 />
<span font-state-field text-4>
<span class="text-gray-400 dark:text-gray-600 group-hover:(text-white op50) [.active_&]:(op50 text-white!)">&lt;</span>
<span group-hover:text-white class="[.active_&]:(text-white)">{{ item.name }}</span>
<span class="text-gray-400 dark:text-gray-600 group-hover:(text-white op50) [.active_&]:(op50 text-white!)">&gt;</span>
</span>
<NodeTag v-for="(_item, index) in item.tags" :key="index" :tag="_item" />

Check warning on line 49 in packages/applet/src/components/tree/TreeViewer.vue

View workflow job for this annotation

GitHub Actions / lint

Variable 'index' is already declared in the upper scope

Check warning on line 49 in packages/applet/src/components/tree/TreeViewer.vue

View workflow job for this annotation

GitHub Actions / ci

Variable 'index' is already declared in the upper scope
</div>
<div
v-if="item?.children?.length && expanded.includes(item.id)"
>
<ComponentTreeViewer v-model="selectedNodeId" :data="item?.children" :depth="depth + 1" />
</div>
</div>
</template>
44 changes: 44 additions & 0 deletions packages/applet/src/composables/devtools-state.ts
@@ -0,0 +1,44 @@
import { getDevToolsState, onDevToolsStateUpdated } from '@vue/devtools-core'
import type { InjectionKey, Ref } from 'vue'
import { inject, provide, ref } from 'vue'

import type { AppRecord } from '@vue/devtools-kit'

export type DevtoolsBridgeAppRecord = Pick<AppRecord, 'name' | 'id' | 'version' | 'routerId' | 'moduleDetectives'>

const VueDevToolsStateSymbol: InjectionKey<{
appRecords: Ref<Array<DevtoolsBridgeAppRecord>>
activeAppRecordId: Ref<string>
}> = Symbol('VueDevToolsStateSymbol')

export function createDevToolsStateContext() {
const appRecords = ref<Array<DevtoolsBridgeAppRecord>>([])
const activeAppRecordId = ref('')

getDevToolsState().then((data) => {
appRecords.value = data!.appRecords
activeAppRecordId.value = data!.activeAppRecordId!
})

onDevToolsStateUpdated((data) => {
appRecords.value = data.appRecords
activeAppRecordId.value = data.activeAppRecordId!
})

provide(VueDevToolsStateSymbol, {
appRecords,
activeAppRecordId,
})

return {
appRecords,
activeAppRecordId,
}
}

export function useDevToolsState() {
return inject(VueDevToolsStateSymbol, {
appRecords: ref([]),
activeAppRecordId: ref(''),
})
}
27 changes: 27 additions & 0 deletions packages/applet/src/composables/select.ts
@@ -0,0 +1,27 @@
import type { InjectionKey, Ref } from 'vue'
import { inject, provide, ref } from 'vue'

const SelectedSymbolKey: InjectionKey<Ref<string>> = Symbol('SelectedSymbolKey')

export function createSelectedContext() {
const selected = ref<string>('')

provide<Ref<string>>(SelectedSymbolKey, selected)

return {
selected,
}
}

export function useSelect() {
const selected = inject<Ref<string>>(SelectedSymbolKey, ref(''))!

function select(value: string) {
selected.value = value
}

return {
selected,
select,
}
}
54 changes: 54 additions & 0 deletions packages/applet/src/composables/shiki.ts
@@ -0,0 +1,54 @@
import type { BuiltinLanguage, HighlighterCore } from 'shiki'
import { getHighlighterCore } from 'shiki/core'
import getWasm from 'shiki/wasm'
import { shallowRef } from 'vue'

export const shiki = shallowRef<HighlighterCore>()

let promise: Promise<any> | null = null

export function renderCodeHighlight(code: string, lang: BuiltinLanguage | 'text' = 'text') {
if (!promise && !shiki.value) {
// Only loading when needed
promise = getHighlighterCore({
themes: [
import('shiki/themes/vitesse-dark.mjs'),
import('shiki/themes/vitesse-light.mjs'),
],
langs: [
import('shiki/langs/json.mjs'),
import('shiki/langs/yaml.mjs'),
import('shiki/langs/css.mjs'),
import('shiki/langs/javascript.mjs'),
import('shiki/langs/typescript.mjs'),
import('shiki/langs/vue.mjs'),
import('shiki/langs/vue-html.mjs'),
import('shiki/langs/html.mjs'),
import('shiki/langs/diff.mjs'),
import('shiki/langs/shellscript.mjs'),
],
loadWasm: getWasm,
}).then((i) => {
shiki.value = i
})
}

const supported = shiki.value?.getLoadedLanguages().includes(lang)
if (!supported) {
return {
code,
supported,
}
}

return {
code: shiki.value!.codeToHtml(code, {
lang,
themes: {
dark: 'vitesse-dark',
light: 'vitesse-light',
},
}),
supported: true,
}
}

0 comments on commit 6359b3e

Please sign in to comment.