Skip to content

Commit

Permalink
feat: UI improvements, highlight file path (#120)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <github@antfu.me>
  • Loading branch information
userquin and antfu authored Aug 22, 2024
1 parent 3ee3a0e commit 441c665
Show file tree
Hide file tree
Showing 21 changed files with 263 additions and 68 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
"lint-staged": "^15.2.9",
"npm-run-all": "^4.1.5",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
"pinia": "^2.2.2",
"prism-theme-vars": "^0.2.5",
"rimraf": "^6.0.1",
Expand Down
5 changes: 5 additions & 0 deletions playground/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import { RouterLink, RouterView } from 'vue-router'
<RouterLink to="/">
Home
</RouterLink>
<span px-1rem>|</span>
<RouterLink to="/other">
Other
</RouterLink>
<span px-1rem>|</span>
<RouterLink to="/error">
Error
</RouterLink>
</nav>

<RouterView />
Expand Down
5 changes: 5 additions & 0 deletions playground/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const router = createRouter({
path: '/other',
component: () => import('../views/Other.vue'),
},
{
name: 'error',
path: '/error',
component: () => import('../views/Error.vue'),
},
],
})

Expand Down
15 changes: 15 additions & 0 deletions playground/src/views/Error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup>
import { onMounted, ref } from 'vue'
const message = ref('Loading...')
onMounted(() => {
import('virtual:slow:error').catch(err => message.value = err.message)
})
</script>

<template>
<div>
<div>Slow Error Message: {{ message }}</div>
</div>
</template>
12 changes: 11 additions & 1 deletion playground/src/views/Other.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
<script setup>
import { onMounted, ref } from 'vue'
import message from 'virtual:hi'
const message2 = ref('Loading...')
onMounted(() => {
import('virtual:slow:2').then(m => message2.value = m.default)
})
</script>

<template>
<div>Other: {{ message }}</div>
<div>
<div>Other: {{ message }}</div>
<div>Slow Message: {{ message2 }}</div>
</div>
</template>
4 changes: 4 additions & 0 deletions playground/src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ declare module 'virtual:hi' {
const value: string
export default value
}
declare module 'virtual:slow:*' {
const value: string
export default value
}
21 changes: 21 additions & 0 deletions playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@ export default defineConfig({
return 'export default \'Hi!\''
},
},
{
name: 'custom-slow-loader',
// for testing purpose, don't change it
enforce: 'post',
resolveId(id) {
return id.startsWith('virtual:slow:') ? `\0${id}` : undefined
},
async load(id) {
if (!id.startsWith('\0virtual:slow:'))
return

const matcher = /^\0virtual:slow:(\d)$/.exec(id)
if (matcher) {
const timeout = +matcher[1]
await new Promise(resolve => setTimeout(resolve, timeout * 1000))
return `export default 'Hi after ${timeout} seconds!'`
}

throw new Error('Invalid timeout!')
},
},
Inspect({
build: true,
open: true,
Expand Down
5 changes: 4 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions src/client/components/DurationDisplay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ function getDurationColor(duration: number | undefined) {
if (!props.color)
return ''
if (duration == null)
return 'text-gray:75'
return ''
duration = duration * props.factor
if (duration < 1)
return 'text-gray:75'
return ''
if (duration > 1000)
return 'text-red-400'
return 'status-red'
if (duration > 500)
return 'text-orange-400'
return 'status-yellow'
if (duration > 200)
return 'text-yellow-400'
return 'status-green'
return ''
}
Expand All @@ -42,7 +42,7 @@ const units = computed(() => {
</script>

<template>
<div :class="getDurationColor(duration)">
{{ units[0] }}<span ml-0.4 text-xs op50>{{ units[1] }}</span>
</div>
<span block>
<span :class="getDurationColor(duration)">{{ units[0] }}</span><span ml-0.4 text-xs :class="getDurationColor(duration)" op75>{{ units[1] }}</span>
</span>
</template>
8 changes: 4 additions & 4 deletions src/client/components/ErrorDisplay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ function normalizeFilename(filename?: string) {

<template>
<div of-auto p4 font-mono flex="~ col gap-4">
<div text-xl text-red7 dark:text-red flex="~ gap-2 items-center">
<div text-xl status-red flex="~ gap-2 items-center">
<div i-carbon:warning-square />
Error
</div>
<pre text-sm text-red7 dark:text-red>{{ error.message }}</pre>
<pre text-sm status-red>{{ error.message }}</pre>
<div border="t main" h-1px w-full />
<div class="text-xs" mt2 grid="~ cols-[max-content_1fr] gap-x-4 gap-y-1" font-mono>
<template v-for="item, idx of error.stack" :key="idx">
<div text-right op50>
<template v-for="(item, idx) of error.stack" :key="idx">
<div text-right op72 dark:op50>
{{ item.functionName || `(anonymous)` }}
</div>
<div ws-nowrap>
Expand Down
6 changes: 3 additions & 3 deletions src/client/components/FilepathItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const display = computed(() => {

<template>
<button flex="~" hover="underline" @click="openInEditor">
<span>{{ display[0] }}</span>
<span op60>{{ display[1] }}</span>
<span v-if="props.line != null && props.column != null" op50>:{{ props.line }}:{{ props.column }}</span>
<span fw-600 dark:fw-unset>{{ display[0] }}</span>
<span op72 dark:op50>{{ display[1] }}</span>
<span v-if="props.line != null && props.column != null" op72 dark:op50>:{{ props.line }}:{{ props.column }}</span>
</button>
</template>
130 changes: 122 additions & 8 deletions src/client/components/ModuleId.vue
Original file line number Diff line number Diff line change
@@ -1,33 +1,147 @@
<script setup lang="ts">
import { computed } from 'vue'
import { vTooltip } from 'floating-vue'
import { relative } from 'pathe'
import { list, root } from '../logic'
import { getPluginColor } from '../logic/color'
const props = withDefaults(
defineProps<{
id?: string
icon?: boolean
module?: boolean
}>(),
{
icon: true,
},
)
const isVirtual = computed(() => list.value?.modules.find(i => i.id === props.id)?.virtual)
const relativePath = computed(() => {
if (!props.id)
return ''
let relate = relative(root.value, props.id)
if (!relate.startsWith('.'))
relate = `./${relate}`
if (relate.startsWith('./'))
return relate
if (relate.match(/^(?:\.\.\/){1,3}[^.]/))
return relate
return props.id
})
const HighlightedPath = defineComponent({
render() {
const parts = relativePath.value.split(/([/?&:])/g)
let type: 'start' | 'path' | 'query' = 'start'
const classes: string[][] = parts.map(() => [])
const nodes = parts.map((part) => {
return h('span', { class: '' }, part)
})
parts.forEach((part, index) => {
const _class = classes[index]
if (part === '?')
type = 'query'
if (type === 'start') {
if (part.match(/^\.+$/)) {
_class.push('op50')
}
else if (part === '/') {
_class.push('op50')
}
else if (part !== '/') {
type = 'path'
}
}
if (type === 'path') {
if (part === '/' || part === 'node_modules' || part.match(/^\.\w/)) {
_class.push('op75')
}
if (part === '.pnpm') {
classes[index + 2]?.push('op50')
if (nodes[index + 2])
nodes[index + 2].children = ''
}
if (part === ':') {
if (nodes[index - 1]) {
nodes[index - 1].props ||= {}
nodes[index - 1].props!.style ||= {}
nodes[index - 1].props!.style.color = getPluginColor(parts[index - 1])
}
_class.push('op50')
}
}
if (type === 'query') {
if (part === '?' || part === '&') {
_class.push('text-rose-5 dark:text-rose-4')
}
else {
_class.push('text-orange-9 dark:text-orange-2')
}
}
})
nodes.forEach((node, index) => {
if (node.props)
node.props.class = classes[index].join(' ')
})
return nodes
},
})
const gridStyles = computed(() => {
if (!props.module)
return ''
const gridColumns: string[] = []
if (props.icon)
gridColumns.push('min-content')
if (props.module)
gridColumns.push('minmax(0,1fr)')
else
gridColumns.push('100%')
// todo: handle slot, not being used
if (isVirtual.value)
gridColumns.push('min-content')
return `grid-template-columns: ${gridColumns.join(' ')};`
})
const containerClass = computed(() => {
return props.module
? 'grid grid-rows-1 items-center gap-1'
: 'flex items-center'
})
</script>

<template>
<div v-if="id" my-auto text-sm font-mono flex="~ items-center">
<div
v-if="id"
v-tooltip.bottom-start="{
content: props.id,
triggers: ['hover', 'focus'],
disabled: !module,
}"
my-auto text-sm font-mono
:class="containerClass"
:style="gridStyles"
>
<FileIcon v-if="icon" :filename="id" mr1.5 />
<template v-if="id.startsWith(root)">
<span class="op50">.</span>
<span>{{ id.slice(root.length) }}</span>
</template>
<span v-else>{{ id }}</span>
<span :class="{ 'overflow-hidden': module, 'text-truncate': module }">
<HighlightedPath />
</span>
<slot />

<Badge
v-if="isVirtual"
class="ml1 bg-teal-400:10 text-green-700 dark:text-teal-400"
class="ml1 badge-virtual"
v-text="'virtual'"
/>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/client/components/ModuleList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,26 @@ function byteToHumanReadable(byte: number) {
:key="i"
>
<span v-if="idx !== 0" op20>|</span>
<span op50>
<span>
<PluginName :name="i.name" :hide="true" />
</span>
</template>
<template v-if="m.data.invokeCount > 2">
<span op40>·</span>
<span
text-green
status-green
:title="`Transform invoked ${m.data.invokeCount} times`"
>x{{ m.data.invokeCount }}</span>
</template>
<div flex-auto />
<span op75>
<span>
<DurationDisplay :duration="m.data.totalTime" />
</span>
<template v-if="m.data.sourceSize && m.data.distSize">
<span op40>·</span>
<span op50>{{ byteToHumanReadable(m.data.sourceSize) }}</span>
<span class="op75 dark:op50">{{ byteToHumanReadable(m.data.sourceSize) }}</span>
<span i-carbon-arrow-right op40 />
<span op50 :class="m.data.distSize > m.data.sourceSize ? 'text-orange' : 'text-green'">{{ byteToHumanReadable(m.data.distSize) }}</span>
<span class="op100 dark:op-65" :class="m.data.distSize > m.data.sourceSize ? 'status-yellow' : 'status-green'">{{ byteToHumanReadable(m.data.distSize) }}</span>
</template>
</div>
</RouterLink>
Expand Down
4 changes: 2 additions & 2 deletions src/client/components/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { isStaticMode, refetch, toggleDark } from '../logic'
<slot />
<slot name="actions">
<button v-if="!isStaticMode" class="text-lg icon-btn" title="Refetch" @click="refetch()">
<div i-carbon-renew />
<span i-carbon-renew block />
</button>
<div h-full w-1 border="r main" />
<a
Expand All @@ -18,7 +18,7 @@ import { isStaticMode, refetch, toggleDark } from '../logic'
<div i-carbon-logo-github />
</a>
<button class="text-lg icon-btn" title="Toggle Dark Mode" @click="toggleDark()">
<div i-carbon-sun dark:i-carbon-moon />
<span i-carbon-sun dark:i-carbon-moon block />
</button>
</slot>
</nav>
Expand Down
Loading

0 comments on commit 441c665

Please sign in to comment.