Skip to content

Commit

Permalink
feat: add nuxt devtools panel (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
arashsheyda committed Mar 10, 2024
1 parent bc11360 commit 57996d7
Show file tree
Hide file tree
Showing 15 changed files with 762 additions and 517 deletions.
1 change: 1 addition & 0 deletions .nuxtrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
typescript.tsConfig.compilerOptions.noUncheckedIndexedAccess=true
modules[]=nuxt-fonts-devtools
207 changes: 207 additions & 0 deletions client/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client'
import type { ClientFunctions, ServerFunctions, ManualFontDetails, ProviderFontDetails } from '../src/devtools'
import { DEVTOOLS_RPC_NAMESPACE } from '../src/constants'
import type { NormalizedFontFaceData, RemoteFontSource } from '../src/types'
type AnnotatedFont = (ManualFontDetails | ProviderFontDetails) & {
css?: string
}
const fonts = ref<AnnotatedFont[]>([])
const search = ref('')
const selected = ref<AnnotatedFont>()
const filtered = computed(() => fonts.value.filter((font) => font.fontFamily.toLowerCase().includes(search.value.toLowerCase())))
onDevtoolsClientConnected(async (client) => {
const rpc = client.devtools.extendClientRpc<ServerFunctions, ClientFunctions>(DEVTOOLS_RPC_NAMESPACE, {
exposeFonts (newFonts) {
fonts.value.push(...newFonts)
},
})
// call server RPC functions
fonts.value = removeDuplicates(await rpc.getFonts())
// TODO: fix this (only testing to see how it'll look like)
for (const family of fonts.value) {
let css = ''
if (!('provider' in family) || family.provider !== 'local') {
for (const font of family.fonts) {
css += await rpc.generateFontFace(family.fontFamily, font) + '\n'
}
}
family.css = css
// add css to document style
window.document.head.appendChild(document.createElement('style')).textContent = css
}
})
function removeDuplicates<T extends Record<string, any>> (array: Array<T>): T[] {
return array.filter((item, index) => index === array.findIndex(other => JSON.stringify(other) === JSON.stringify(item)))
}
function prettyURL (font: NormalizedFontFaceData) {
const firstRemoteSource = font.src.find((i): i is RemoteFontSource => 'url' in i)
if (firstRemoteSource) {
return firstRemoteSource.originalURL || firstRemoteSource.url
}
}
</script>

<template>
<main
:grid="`~ ${selected ? 'cols-2' : 'cols-1'}`"
class="h-screen of-hidden"
>
<div class="of-auto">
<NNavbar v-model:search="search">
<!-- TODO: add support for editing fonts config -->
<!-- <template #actions>
<NButton
title="Fonts Config"
class="h-full"
n="orange xl"
icon="i-carbon-settings"
/>
</template> -->
<div class="text-xs">
<span
v-if="search"
class="op-40"
>
{{ filtered.length }} matched ·
</span>
<span class="op-40">
{{ fonts.length }} fonts in total
</span>
</div>
</NNavbar>
<div
:grid="`~ ${selected ? 'cols-3' : 'cols-5'}`"
class="p-4 gap-4 text-center"
>
<NCard
v-for="family of filtered"
:key="family.fontFamily"
:title="family.fontFamily"
class="truncate text-gray-500/75 p-4 cursor-pointer hover:bg-active"
:class="{ 'bg-active!': selected === family }"
@click="selected = family"
>
<h1
text="white 5xl"
class="mb-2"
:style="{ fontFamily: family.fontFamily }"
>
Aa
</h1>
<small>
{{ family.fontFamily }}
</small>
</NCard>
</div>
</div>
<div
v-if="selected"
class="border-l border-base of-auto"
>
<NNavbar>
<template #actions>
<div class="flex justify-between items-center w-full py-2">
<div
class="font-bold flex items-center gap-2"
:style="{ fontFamily: selected.fontFamily }"
>
<NBadge>
<NIcon icon="i-carbon-text-font" />
</NBadge>
{{ selected.fontFamily }}
</div>
<div class="flex items-center gap-2">
<NBadge
v-if="'provider' in selected"
class="flex items-center gap-2 px-3 py-1"
title="Provider"
n="green"
>
<NIcon icon="i-carbon-load-balancer-global" />
{{ selected.provider }}
</NBadge>
<NButton
n="red"
icon="i-carbon-close-large"
@click="selected = undefined"
/>
</div>
</div>
</template>
</NNavbar>
<div class="p-4">
<section class="border-b border-base mb-4 pb-4">
<span class="op-40">
Properties:
</span>
<div v-if="selected.type">
type: {{ selected.type }}
</div>
<div v-if="'provider' in selected">
provider: {{ selected.provider }}
</div>
</section>
<section class="border-b border-base mb-4 pb-4 flex flex-col gap-4">
<span class="op-40">
Fonts:
</span>
<div
v-for="font, index of selected.fonts"
:key="index"
class="flex justify-between items-center gap-2"
>
<div class="font-mono text-xs flex flex-col gap-1">
<span class="line-clamp-1">
{{ prettyURL(font) }}
</span>
<span class="flex flex-row gap-1 opacity-75">
{{ font.style || 'normal' }}
<span class="flex flex-row gap-1">
{{ Array.isArray(font.weight) ? font.weight.join('-') : font.weight }}
</span>
</span>
<span
v-if="font.unicodeRange"
class="line-clamp-1 opacity-75"
>
<NIcon icon="i-carbon:language" />
{{ font.unicodeRange?.join(', ') }}
</span>
</div>
<NButton
n="sm blue"
icon="i-carbon-download"
download
target="_blank"
external
:to="font.src.find((i): i is { url: string } => 'url' in i)?.url"
/>
</div>
</section>
<section class="mb-4 pb-4 flex flex-col gap-4">
<span class="op-40">
Generated CSS:
</span>
<NCodeBlock
v-if="selected.css"
:code="selected.css"
/>
<!-- dev only, will remove it later -->
<!-- <NCodeBlock
:code="JSON.stringify(selected, null, 2)"
/> -->
</section>
</div>
</div>
</main>
</template>
22 changes: 22 additions & 0 deletions client/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createResolver, defineNuxtModule } from '@nuxt/kit'
import { startSubprocess } from '@nuxt/devtools-kit'

import { DEVTOOLS_UI_PORT } from '../src/constants'

const resolver = createResolver(import.meta.url)

export default defineNuxtModule((_, nuxt) => {
if (!nuxt.options.dev || !nuxt.options.modules?.includes('@nuxt/fonts')) return

startSubprocess(
{
command: 'npx',
args: ['nuxi', 'dev', '--port', DEVTOOLS_UI_PORT.toString()],
cwd: resolver.resolve('.'),
},
{
id: 'nuxt-devtools:fonts-client',
name: 'Nuxt DevTools Fonts Client',
},
)
})
28 changes: 28 additions & 0 deletions client/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createResolver } from '@nuxt/kit'
import { DEVTOOLS_UI_PATH } from '../src/constants'

const resolver = createResolver(import.meta.url)

export default defineNuxtConfig({
ssr: false,
modules: ['@nuxt/devtools-ui-kit'],
unocss: {
icons: true,
shortcuts: {
'bg-base': 'bg-white dark:bg-[#151515]',
'bg-active': 'bg-gray:5',
'bg-hover': 'bg-gray:3',
'border-base': 'border-gray/20',
'glass-effect': 'backdrop-blur-6 bg-white/80 dark:bg-[#151515]/90',
'navbar-glass': 'sticky z-10 top-0 glass-effect',
},
},
nitro: {
output: {
publicDir: resolver.resolve('../dist/client'),
},
},
app: {
baseURL: DEVTOOLS_UI_PATH,
},
})
22 changes: 22 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "nuxt-fonts-devtools",
"private": true,
"type": "module",
"scripts": {
"dev": "nuxi dev",
"build": "nuxi build",
"generate": "nuxi generate"
},
"exports": {
".": "./bootstrap.ts"
},
"devDependencies": {
"@iconify-json/carbon": "^1.1.31",
"@nuxt/devtools-kit": "^1.0.8",
"@nuxt/devtools-ui-kit": "latest",
"@nuxt/kit": "^3.10.3",
"nuxt": "latest",
"vue": "latest",
"vue-router": "latest"
}
}
3 changes: 3 additions & 0 deletions client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@
"utils.d.ts"
],
"scripts": {
"build": "nuxt-module-build build",
"build": "nuxt-module-build build && pnpm client:build",
"prepack": "pnpm build",
"client:build": "nuxi generate client",
"client:dev": "nuxi dev client --port 3300",
"dev": "nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi prepare client",
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
"lint": "eslint .",
"test": "vitest run",
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit && cd ../client && vue-tsc --noEmit",
"test:watch": "vitest watch"
},
"dependencies": {
"@nuxt/devtools-kit": "^1.0.8",
"@nuxt/kit": "^3.10.3",
"chalk": "^5.3.0",
"css-tree": "^2.3.1",
Expand All @@ -52,6 +55,7 @@
"ofetch": "^1.3.3",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
"sirv": "^2.0.4",
"ufo": "^1.4.0",
"unplugin": "^1.9.0",
"unstorage": "^1.10.1"
Expand All @@ -64,12 +68,14 @@
"@nuxt/test-utils": "^3.11.0",
"@types/css-tree": "^2.3.7",
"@vitest/coverage-v8": "^1.3.1",
"birpc": "^0.2.17",
"changelogen": "^0.5.5",
"consola": "^3.2.3",
"eslint": "^8.57.0",
"execa": "^8.0.1",
"nitropack": "^2.9.3",
"nuxt": "^3.10.3",
"nuxt-fonts-devtools": "workspace:^",
"playwright-core": "^1.42.1",
"semver": "^7.6.0",
"typescript": "^5.4.2",
Expand All @@ -81,4 +87,4 @@
"resolutions": {
"@nuxt/fonts": "workspace:*"
}
}
}
1 change: 1 addition & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxt/fonts', '@nuxtjs/tailwindcss', '@unocss/nuxt'],
unocss: {
disableNuxtInlineStyle: false,
Expand Down
Loading

0 comments on commit 57996d7

Please sign in to comment.