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: applet package #283

Merged
merged 44 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
84b225c
feat: applet
webfansplz Feb 7, 2024
ee17eb4
chore: update
webfansplz Feb 7, 2024
5a174a6
Merge branch 'main' into feat-applet
webfansplz Feb 10, 2024
7401354
chore: update
webfansplz Feb 10, 2024
a1d2910
feat: applet package
webfansplz Feb 13, 2024
80ddda4
chore: update
webfansplz Feb 13, 2024
69862e0
chore: update
webfansplz Feb 16, 2024
99dd8af
chore: update
webfansplz Feb 20, 2024
cb6b841
feat: pinia timeline
webfansplz Feb 23, 2024
f642d18
chore: lint
webfansplz Feb 23, 2024
f0bf318
feat: virtual router
webfansplz Feb 23, 2024
856c34d
chore: update
webfansplz Feb 23, 2024
e1759ff
feat: pinia store
webfansplz Feb 24, 2024
25a554f
chore: update
webfansplz Mar 2, 2024
890f391
feat(applet): devtools header component
webfansplz Mar 2, 2024
0b7ec34
chore: update
webfansplz Mar 2, 2024
7e9a900
chore: update
webfansplz Mar 2, 2024
590e748
fix(applet): tsconfig
webfansplz Mar 2, 2024
6521c4d
chore: update
webfansplz Mar 3, 2024
55b0004
chore: update
webfansplz Mar 3, 2024
3b1a18c
chore: update
webfansplz Mar 3, 2024
4a836b5
chore: update
webfansplz Mar 4, 2024
e7eb27a
chore: update
webfansplz Mar 4, 2024
6c51773
chore: update
webfansplz Mar 4, 2024
9237346
chore: update
webfansplz Mar 5, 2024
6b00a4d
chore: update
webfansplz Mar 5, 2024
60fd411
chore: update
webfansplz Mar 5, 2024
c48a0a4
chore: update
webfansplz Mar 6, 2024
a8f6a57
chore: update
webfansplz Mar 8, 2024
af8a0ce
feat(applet): connect state composable
webfansplz Mar 8, 2024
399d38f
chore: update
webfansplz Mar 13, 2024
299d77b
Merge branch 'main' into feat-applet
webfansplz Mar 16, 2024
7b4f3ba
feat(kit): optimize editing on native set
webfansplz Mar 16, 2024
0bc6251
chore: update
webfansplz Mar 16, 2024
baa3624
Merge branch 'main' into feat-applet
webfansplz Mar 16, 2024
c775e80
chore: update
webfansplz Mar 16, 2024
ca63b7a
chore: update
webfansplz Mar 16, 2024
330411c
chore: update
webfansplz Mar 16, 2024
fa251a2
chore: update
webfansplz Mar 17, 2024
f726c6e
chore: fix type
webfansplz Mar 17, 2024
b0e69b2
chore: fix lock
webfansplz Mar 17, 2024
592163d
chore: update
webfansplz Mar 17, 2024
23e4d42
chore: update
webfansplz Mar 17, 2024
8103825
chore: update
webfansplz Mar 17, 2024
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"dev:browser-extension": "turbo dev --filter=./packages/browser-extension",
"dev:ui-story": "turbo dev --filter=./packages/ui-story",
"prepare:type": "turbo prepare:type --filter='./packages/*'",
"dev": "NODE_OPTIONS=\"--max-old-space-size=8192\" nr prepare:type && nr build:ui && turbo stub",
"dev": "NODE_OPTIONS=\"--max-old-space-size=8192\" nr prepare:type && nr build:ui && turbo stub --concurrency 20",
"build": "turbo build",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
Expand Down Expand Up @@ -84,6 +84,7 @@
"eslint": "npm:eslint-ts-patch@8.55.0-1",
"eslint-plugin-format": "^0.1.0",
"eslint-ts-patch": "8.55.0-1",
"execa": "^8.0.1",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"jsdom": "^24.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/applet/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src/index'
43 changes: 43 additions & 0 deletions packages/applet/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@vue/devtools-applet",
"type": "module",
"version": "7.0.17",
"author": "webfansplz",
"license": "MIT",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./style.css": "./dist/index.css"
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"files": [
"**.d.ts",
"dist"
],
"scripts": {
"build": "vite build && pnpm types",
"prepare:type": "pnpm types",
"stub": "vite build --watch",
"types": "vue-tsc --declaration --emitDeclarationOnly -p ./tsconfig.json"
},
"peerDependencies": {
"vue": "^3.0.0"
},
"dependencies": {
"@vue/devtools-core": "workspace:^",
"@vue/devtools-kit": "workspace:^",
"@vue/devtools-shared": "workspace:^",
"@vue/devtools-ui": "workspace:^",
"perfect-debounce": "^1.0.0",
"splitpanes": "^3.1.5"
},
"devDependencies": {
"unplugin-vue": "^5.0.3",
"vue": "^3.4.15",
"vue-router": "^4.2.5",
"vue-tsc": "^1.8.27"
}
}
24 changes: 24 additions & 0 deletions packages/applet/src/components/basic/DevToolsHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { defineProps } from 'vue'
import { useVirtualRouter } from '~/composables/virtual-router'

defineProps<{
githubRepoLink: string
docLink: string
}>()
const router = useVirtualRouter()
</script>

<template>
<div border="b base" class="h10 h40px flex items-center justify-between px-2">
<i class="i-ep:back cursor-pointer op70 text-base hover:op100" @click="router.push('/')" />
<div>
<a class="pr1" :href="docLink" target="_blank">
<i class="i-clarity:document-line cursor-pointer op70 text-base hover:op100" />
</a>
<a :href="githubRepoLink" target="_blank">
<i class="i-mdi:github cursor-pointer op70 text-base hover:op100" />
</a>
</div>
</div>
</template>
8 changes: 8 additions & 0 deletions packages/applet/src/components/basic/Empty.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<div class="h-full flex flex-col items-center justify-center op50">
<i class="i-lets-icons:blank-light" />
<span>
<slot />
</span>
</div>
</template>
24 changes: 24 additions & 0 deletions packages/applet/src/components/basic/SelectiveList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { defineModel } from 'vue'

defineProps<{ data: { id: string, label: string }[] }>()

const selected = defineModel()

function select(id: string) {
selected.value = id
}
</script>

<template>
<ul class="p2">
<li
v-for="item in data" :key="item.id"
class="selectable-item"
:class="{ active: item.id === selected }"
@click="select(item.id)"
>
{{ item.label }}
</li>
</ul>
</template>
14 changes: 14 additions & 0 deletions packages/applet/src/components/basic/ToggleExpanded.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
defineProps<{
value: boolean
}>()
</script>

<template>
<i
class="i-radix-icons:triangle-right flex-none text-4 op-50 transition-base"
:class="{
'transform rotate-90': value,
}"
/>
</template>
23 changes: 23 additions & 0 deletions packages/applet/src/components/state/ChildStateViewer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import type { InspectorState } from '@vue/devtools-kit'
import StateFieldViewer from './StateFieldViewer.vue'

withDefaults(defineProps<{
data: InspectorState[]
depth: number
index: string
}>(), {
depth: 0,
})
</script>

<template>
<div>
<div
v-for="(item, i) in data"
:key="i"
>
<StateFieldViewer :data="item" :depth="depth + 1" :index="`${index}-${i}`" />
</div>
</div>
</template>
70 changes: 70 additions & 0 deletions packages/applet/src/components/state/RootStateViewer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script setup lang="ts">
import { watchEffect } from 'vue'
import type { InspectorState } from '@vue/devtools-kit'
import ChildStateViewer from './ChildStateViewer.vue'
import ToggleExpanded from '~/components/basic/ToggleExpanded.vue'
import { useToggleExpanded } from '~/composables/toggle-expanded'
import { createStateEditorContext } from '~/composables/state-editor'

const props = withDefaults(defineProps<{
data: Record<string, InspectorState[]>
nodeId: string
inspectorId: string
disableEdit?: boolean
}>(), {
disableEdit: false,
})

function initEditorContext() {
return {
nodeId: props.nodeId,
inspectorId: props.inspectorId,
disableEdit: props.disableEdit,
}
}

const { context } = createStateEditorContext(initEditorContext())
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}`)
})
})
</script>

<template>
<div>
<div
v-for="(item, key, index) in data"
:key="index"
>
<div
class="flex items-center"
:class="[item?.length && 'cursor-pointer hover:(bg-active)']"
@click="toggleExpanded(`${index}`)"
>
<ToggleExpanded
v-if="item?.length"
:value="expanded.includes(`${index}`)"
/>
<!-- placeholder -->
<span v-else pl5 />
<span font-state-field text-4>
{{ key }}
</span>
</div>
<div
v-if="item?.length && expanded.includes(`${index}`)"
>
<ChildStateViewer :data="item" :index="`${index}`" />
</div>
</div>
</div>
</template>
165 changes: 165 additions & 0 deletions packages/applet/src/components/state/StateFieldEditor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<script setup lang="ts">
import { computed, ref, toRaw } from 'vue'
import { VueButton, VueDropdown, VueDropdownButton, VueIcon, VTooltip as vTooltip } from '@vue/devtools-ui'
import { getRaw } from '@vue/devtools-kit'
import type { InspectorState, InspectorStateEditorPayload } from '@vue/devtools-kit'
import type { ButtonProps } from '@vue/devtools-ui/dist/types/src/components/Button'
import { editInspectorState } from '@vue/devtools-core'
import { useClipboard } from '@vueuse/core'
import { useStateEditorContext } from '~/composables/state-editor'
import type { EditorAddNewPropType, EditorInputValidType } from '~/composables/state-editor'

const props = withDefaults(defineProps<{
data: InspectorState
hovering: boolean
depth: number
showAddIfNeeded?: boolean
disableEdit?: boolean
}>(), {
showAddIfNeeded: true,
})

defineEmits<{
enableEditInput: [type: EditorInputValidType]
addNewProp: [type: EditorAddNewPropType]
}>()

const state = useStateEditorContext()

const { copy, isSupported } = useClipboard()

const popupVisible = ref(false)

const raw = computed(() => getRaw(props.data.value))
const rawValue = computed(() => raw.value.value)
const customType = computed(() => raw.value.customType)
const dataType = computed(() => rawValue.value === null ? 'null' : typeof rawValue.value)

const iconButtonProps = {
flat: true,
size: 'mini',
} satisfies ButtonProps

const buttonClass = computed(() => ({
'opacity-0': !props.hovering,
}))

function quickEdit(v: unknown, remove: boolean = false) {
editInspectorState({
path: props.data.key.split('.'),
inspectorId: state.value.inspectorId,
type: props.data.stateType!,
nodeId: state.value.nodeId,
state: {
newKey: null!,
value: toRaw(v),
type: dataType.value,
remove,
},
} satisfies InspectorStateEditorPayload)
}

function quickEditNum(v: number | string, offset: 1 | -1) {
const target = typeof v === 'number'
? v + offset
: BigInt(v) + BigInt(offset)
quickEdit(target)
}
</script>

<template>
<div class="inline pl5px">
<!-- only editable will show operate actions -->
<template v-if="!props.disableEdit && data.editable">
<!-- input edit, number/string/object -->
<template v-if="dataType === 'string' || dataType === 'number' || dataType === 'object' || dataType === 'null'">
<VueButton
v-tooltip="{
content: 'Edit value',
}" v-bind="iconButtonProps" :class="buttonClass" @click.stop="$emit('enableEditInput', dataType)"
>
<template #icon>
<VueIcon icon="i-material-symbols-edit-rounded" />
</template>
</VueButton>
<VueButton
v-if="dataType === 'object' && showAddIfNeeded"
v-tooltip="{
content: 'Add new value',
}" v-bind="iconButtonProps" :class="buttonClass" @click.stop="
$emit('addNewProp', Array.isArray(rawValue) ? 'array' : 'object')"
>
<template #icon>
<VueIcon icon="i-material-symbols-add-circle-rounded" />
</template>
</VueButton>
</template>
<!-- checkbox, button value only -->
<VueButton
v-if="dataType === 'boolean'" v-bind="iconButtonProps" :class="buttonClass"
@click="quickEdit(!rawValue)"
>
<template #icon>
<VueIcon :icon="rawValue ? 'i-material-symbols-check-box-sharp' : 'i-material-symbols-check-box-outline-blank-sharp'" />
</template>
</VueButton>
<!-- increment/decrement button, numeric/bigint -->
<template v-else-if="dataType === 'number' || customType === 'bigint'">
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEditNum(rawValue as number | string, 1)">
<template #icon>
<VueIcon icon="i-carbon-add" />
</template>
</VueButton>
<VueButton v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEditNum(rawValue as number | string, -1)">
<template #icon>
<VueIcon icon="i-carbon-subtract" />
</template>
</VueButton>
</template>
</template>
<!-- delete prop, only appear if depth > 0 -->
<VueButton v-if="!props.disableEdit && depth > 0" v-bind="iconButtonProps" :class="buttonClass" @click.stop="quickEdit(rawValue, true)">
<template #icon>
<VueIcon icon="i-material-symbols-delete-rounded" />
</template>
</VueButton>
<!-- Copy key/value -->
<VueDropdown
:class="{
'opacity-0': !hovering && !popupVisible,
}"
:button-props="{
flat: true,
size: 'mini',
}"
:disabled="!isSupported"
@update:visible="v => popupVisible = v"
>
<template #popper>
<div class="w160px py5px">
<VueDropdownButton
@click="copy(typeof rawValue === 'object' ? JSON.stringify(rawValue) : rawValue.toString())"
>
<template #icon>
<VueIcon icon="i-material-symbols-copy-all-rounded" class="mt4px" />
Copy Value
</template>
</VueDropdownButton>
<VueDropdownButton
@click="() => {
copy(data.key)
}"
>
<template #icon>
<VueIcon icon="i-material-symbols-copy-all-rounded" class="mt4px" />
Copy Path
</template>
</VueDropdownButton>
</div>
</template>
<template #button-icon>
<VueIcon icon="i-material-symbols:more-vert" />
</template>
</VueDropdown>
</div>
</template>
Loading
Loading