Skip to content

Commit 4cbc985

Browse files
authored
feat: applet package (#283)
1 parent bb57ec2 commit 4cbc985

Some content is hidden

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

43 files changed

+1970
-208
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"dev:browser-extension": "turbo dev --filter=./packages/browser-extension",
4545
"dev:ui-story": "turbo dev --filter=./packages/ui-story",
4646
"prepare:type": "turbo prepare:type --filter='./packages/*'",
47-
"dev": "NODE_OPTIONS=\"--max-old-space-size=8192\" nr prepare:type && nr build:ui && turbo stub",
47+
"dev": "NODE_OPTIONS=\"--max-old-space-size=8192\" nr prepare:type && nr build:ui && turbo stub --concurrency 20",
4848
"build": "turbo build",
4949
"lint": "eslint .",
5050
"lint:fix": "eslint . --fix",
@@ -84,6 +84,7 @@
8484
"eslint": "npm:eslint-ts-patch@8.55.0-1",
8585
"eslint-plugin-format": "^0.1.0",
8686
"eslint-ts-patch": "8.55.0-1",
87+
"execa": "^8.0.1",
8788
"fast-glob": "^3.3.2",
8889
"fs-extra": "^11.2.0",
8990
"jsdom": "^24.0.0",

packages/applet/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src/index'

packages/applet/package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "@vue/devtools-applet",
3+
"type": "module",
4+
"version": "7.0.17",
5+
"author": "webfansplz",
6+
"license": "MIT",
7+
"exports": {
8+
".": {
9+
"import": "./dist/index.js",
10+
"require": "./dist/index.cjs"
11+
},
12+
"./style.css": "./dist/index.css"
13+
},
14+
"main": "./dist/index.cjs",
15+
"module": "./dist/index.js",
16+
"files": [
17+
"**.d.ts",
18+
"dist"
19+
],
20+
"scripts": {
21+
"build": "vite build && pnpm types",
22+
"prepare:type": "pnpm types",
23+
"stub": "vite build --watch",
24+
"types": "vue-tsc --declaration --emitDeclarationOnly -p ./tsconfig.json"
25+
},
26+
"peerDependencies": {
27+
"vue": "^3.0.0"
28+
},
29+
"dependencies": {
30+
"@vue/devtools-core": "workspace:^",
31+
"@vue/devtools-kit": "workspace:^",
32+
"@vue/devtools-shared": "workspace:^",
33+
"@vue/devtools-ui": "workspace:^",
34+
"perfect-debounce": "^1.0.0",
35+
"splitpanes": "^3.1.5"
36+
},
37+
"devDependencies": {
38+
"unplugin-vue": "^5.0.3",
39+
"vue": "^3.4.15",
40+
"vue-router": "^4.2.5",
41+
"vue-tsc": "^1.8.27"
42+
}
43+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script setup lang="ts">
2+
import { defineProps } from 'vue'
3+
import { useVirtualRouter } from '~/composables/virtual-router'
4+
5+
defineProps<{
6+
githubRepoLink: string
7+
docLink: string
8+
}>()
9+
const router = useVirtualRouter()
10+
</script>
11+
12+
<template>
13+
<div border="b base" class="h10 h40px flex items-center justify-between px-2">
14+
<i class="i-ep:back cursor-pointer op70 text-base hover:op100" @click="router.push('/')" />
15+
<div>
16+
<a class="pr1" :href="docLink" target="_blank">
17+
<i class="i-clarity:document-line cursor-pointer op70 text-base hover:op100" />
18+
</a>
19+
<a :href="githubRepoLink" target="_blank">
20+
<i class="i-mdi:github cursor-pointer op70 text-base hover:op100" />
21+
</a>
22+
</div>
23+
</div>
24+
</template>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<template>
2+
<div class="h-full flex flex-col items-center justify-center op50">
3+
<i class="i-lets-icons:blank-light" />
4+
<span>
5+
<slot />
6+
</span>
7+
</div>
8+
</template>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script setup lang="ts">
2+
import { defineModel } from 'vue'
3+
4+
defineProps<{ data: { id: string, label: string }[] }>()
5+
6+
const selected = defineModel()
7+
8+
function select(id: string) {
9+
selected.value = id
10+
}
11+
</script>
12+
13+
<template>
14+
<ul class="p2">
15+
<li
16+
v-for="item in data" :key="item.id"
17+
class="selectable-item"
18+
:class="{ active: item.id === selected }"
19+
@click="select(item.id)"
20+
>
21+
{{ item.label }}
22+
</li>
23+
</ul>
24+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
value: boolean
4+
}>()
5+
</script>
6+
7+
<template>
8+
<i
9+
class="i-radix-icons:triangle-right flex-none text-4 op-50 transition-base"
10+
:class="{
11+
'transform rotate-90': value,
12+
}"
13+
/>
14+
</template>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script setup lang="ts">
2+
import type { InspectorState } from '@vue/devtools-kit'
3+
import StateFieldViewer from './StateFieldViewer.vue'
4+
5+
withDefaults(defineProps<{
6+
data: InspectorState[]
7+
depth: number
8+
index: string
9+
}>(), {
10+
depth: 0,
11+
})
12+
</script>
13+
14+
<template>
15+
<div>
16+
<div
17+
v-for="(item, i) in data"
18+
:key="i"
19+
>
20+
<StateFieldViewer :data="item" :depth="depth + 1" :index="`${index}-${i}`" />
21+
</div>
22+
</div>
23+
</template>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<script setup lang="ts">
2+
import { watchEffect } from 'vue'
3+
import type { InspectorState } from '@vue/devtools-kit'
4+
import ChildStateViewer from './ChildStateViewer.vue'
5+
import ToggleExpanded from '~/components/basic/ToggleExpanded.vue'
6+
import { useToggleExpanded } from '~/composables/toggle-expanded'
7+
import { createStateEditorContext } from '~/composables/state-editor'
8+
9+
const props = withDefaults(defineProps<{
10+
data: Record<string, InspectorState[]>
11+
nodeId: string
12+
inspectorId: string
13+
disableEdit?: boolean
14+
}>(), {
15+
disableEdit: false,
16+
})
17+
18+
function initEditorContext() {
19+
return {
20+
nodeId: props.nodeId,
21+
inspectorId: props.inspectorId,
22+
disableEdit: props.disableEdit,
23+
}
24+
}
25+
26+
const { context } = createStateEditorContext(initEditorContext())
27+
watchEffect(() => {
28+
context.value = initEditorContext()
29+
})
30+
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+
})
40+
</script>
41+
42+
<template>
43+
<div>
44+
<div
45+
v-for="(item, key, index) in data"
46+
:key="index"
47+
>
48+
<div
49+
class="flex items-center"
50+
:class="[item?.length && 'cursor-pointer hover:(bg-active)']"
51+
@click="toggleExpanded(`${index}`)"
52+
>
53+
<ToggleExpanded
54+
v-if="item?.length"
55+
:value="expanded.includes(`${index}`)"
56+
/>
57+
<!-- placeholder -->
58+
<span v-else pl5 />
59+
<span font-state-field text-4>
60+
{{ key }}
61+
</span>
62+
</div>
63+
<div
64+
v-if="item?.length && expanded.includes(`${index}`)"
65+
>
66+
<ChildStateViewer :data="item" :index="`${index}`" />
67+
</div>
68+
</div>
69+
</div>
70+
</template>
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<script setup lang="ts">
2+
import { computed, ref, toRaw } from 'vue'
3+
import { VueButton, VueDropdown, VueDropdownButton, VueIcon, VTooltip as vTooltip } from '@vue/devtools-ui'
4+
import { getRaw } from '@vue/devtools-kit'
5+
import type { InspectorState, InspectorStateEditorPayload } from '@vue/devtools-kit'
6+
import type { ButtonProps } from '@vue/devtools-ui/dist/types/src/components/Button'
7+
import { editInspectorState } from '@vue/devtools-core'
8+
import { useClipboard } from '@vueuse/core'
9+
import { useStateEditorContext } from '~/composables/state-editor'
10+
import type { EditorAddNewPropType, EditorInputValidType } from '~/composables/state-editor'
11+
12+
const props = withDefaults(defineProps<{
13+
data: InspectorState
14+
hovering: boolean
15+
depth: number
16+
showAddIfNeeded?: boolean
17+
disableEdit?: boolean
18+
}>(), {
19+
showAddIfNeeded: true,
20+
})
21+
22+
defineEmits<{
23+
enableEditInput: [type: EditorInputValidType]
24+
addNewProp: [type: EditorAddNewPropType]
25+
}>()
26+
27+
const state = useStateEditorContext()
28+
29+
const { copy, isSupported } = useClipboard()
30+
31+
const popupVisible = ref(false)
32+
33+
const raw = computed(() => getRaw(props.data.value))
34+
const rawValue = computed(() => raw.value.value)
35+
const customType = computed(() => raw.value.customType)
36+
const dataType = computed(() => rawValue.value === null ? 'null' : typeof rawValue.value)
37+
38+
const iconButtonProps = {
39+
flat: true,
40+
size: 'mini',
41+
} satisfies ButtonProps
42+
43+
const buttonClass = computed(() => ({
44+
'opacity-0': !props.hovering,
45+
}))
46+
47+
function quickEdit(v: unknown, remove: boolean = false) {
48+
editInspectorState({
49+
path: props.data.key.split('.'),
50+
inspectorId: state.value.inspectorId,
51+
type: props.data.stateType!,
52+
nodeId: state.value.nodeId,
53+
state: {
54+
newKey: null!,
55+
value: toRaw(v),
56+
type: dataType.value,
57+
remove,
58+
},
59+
} satisfies InspectorStateEditorPayload)
60+
}
61+
62+
function quickEditNum(v: number | string, offset: 1 | -1) {
63+
const target = typeof v === 'number'
64+
? v + offset
65+
: BigInt(v) + BigInt(offset)
66+
quickEdit(target)
67+
}
68+
</script>
69+
70+
<template>
71+
<div class="inline pl5px">
72+
<!-- only editable will show operate actions -->
73+
<template v-if="!props.disableEdit && data.editable">
74+
<!-- input edit, number/string/object -->
75+
<template v-if="dataType === 'string' || dataType === 'number' || dataType === 'object' || dataType === 'null'">
76+
<VueButton
77+
v-tooltip="{
78+
content: 'Edit value',
79+
}" v-bind="iconButtonProps" :class="buttonClass" @click.stop="$emit('enableEditInput', dataType)"
80+
>
81+
<template #icon>
82+
<VueIcon icon="i-material-symbols-edit-rounded" />
83+
</template>
84+
</VueButton>
85+
<VueButton
86+
v-if="dataType === 'object' && showAddIfNeeded"
87+
v-tooltip="{
88+
content: 'Add new value',
89+
}" v-bind="iconButtonProps" :class="buttonClass" @click.stop="
90+
$emit('addNewProp', Array.isArray(rawValue) ? 'array' : 'object')"
91+
>
92+
<template #icon>
93+
<VueIcon icon="i-material-symbols-add-circle-rounded" />
94+
</template>
95+
</VueButton>
96+
</template>
97+
<!-- checkbox, button value only -->
98+
<VueButton
99+
v-if="dataType === 'boolean'" v-bind="iconButtonProps" :class="buttonClass"
100+
@click="quickEdit(!rawValue)"
101+
>
102+
<template #icon>
103+
<VueIcon :icon="rawValue ? 'i-material-symbols-check-box-sharp' : 'i-material-symbols-check-box-outline-blank-sharp'" />
104+
</template>
105+
</VueButton>
106+
<!-- increment/decrement button, numeric/bigint -->
107+
<template v-else-if="dataType === 'number' || customType === 'bigint'">
108+
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEditNum(rawValue as number | string, 1)">
109+
<template #icon>
110+
<VueIcon icon="i-carbon-add" />
111+
</template>
112+
</VueButton>
113+
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEditNum(rawValue as number | string, -1)">
114+
<template #icon>
115+
<VueIcon icon="i-carbon-subtract" />
116+
</template>
117+
</VueButton>
118+
</template>
119+
</template>
120+
<!-- delete prop, only appear if depth > 0 -->
121+
<VueButton v-if="!props.disableEdit && depth > 0" v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEdit(rawValue, true)">
122+
<template #icon>
123+
<VueIcon icon="i-material-symbols-delete-rounded" />
124+
</template>
125+
</VueButton>
126+
<!-- Copy key/value -->
127+
<VueDropdown
128+
:class="{
129+
'opacity-0': !hovering && !popupVisible,
130+
}"
131+
:button-props="{
132+
flat: true,
133+
size: 'mini',
134+
}"
135+
:disabled="!isSupported"
136+
@update:visible="v => popupVisible = v"
137+
>
138+
<template #popper>
139+
<div class="w160px py5px">
140+
<VueDropdownButton
141+
@click="copy(typeof rawValue === 'object' ? JSON.stringify(rawValue) : rawValue.toString())"
142+
>
143+
<template #icon>
144+
<VueIcon icon="i-material-symbols-copy-all-rounded" class="mt4px" />
145+
Copy Value
146+
</template>
147+
</VueDropdownButton>
148+
<VueDropdownButton
149+
@click="() => {
150+
copy(data.key)
151+
}"
152+
>
153+
<template #icon>
154+
<VueIcon icon="i-material-symbols-copy-all-rounded" class="mt4px" />
155+
Copy Path
156+
</template>
157+
</VueDropdownButton>
158+
</div>
159+
</template>
160+
<template #button-icon>
161+
<VueIcon icon="i-material-symbols:more-vert" />
162+
</template>
163+
</VueDropdown>
164+
</div>
165+
</template>

0 commit comments

Comments
 (0)