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: nuxt devtools initial setup #45

Merged
merged 10 commits into from
Mar 10, 2024
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
Loading