diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 46c06b2a3..9da51cf5a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,8 +1,8 @@ { - "name": "FMHYedit", - "postStartCommand": "pnpm install", - "postAttachCommand": "pnpm docs:dev", "appPort": 5173, + "features": {}, "image": "mcr.microsoft.com/devcontainers/universal:2", - "features": {} + "name": "FMHYedit", + "postAttachCommand": "pnpm docs:dev", + "postStartCommand": "pnpm install" } diff --git a/.mise.toml b/.mise.toml index a6b9ba729..5d3cdd5a1 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,3 +1,9 @@ # https://github.com/vitejs/vite/issues/17291 [tools] node = "21" + +[tasks] +d = "nrr docs:dev --host" +b = "nrr docs:build" +s = "nrr docs:preview --host" +bb = "nrr docs:build && nrr docs:preview --host" diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 88e0a1580..762f32771 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -4,3 +4,6 @@ singleQuote: true printWidth: 80 trailingComma: none htmlWhitespaceSensitivity: ignore +plugins: + - prettier-plugin-tailwindcss + - prettier-plugin-pkgsort diff --git a/api/routes/feedback.post.ts b/api/routes/feedback.post.ts index 43589111d..89dc83500 100644 --- a/api/routes/feedback.post.ts +++ b/api/routes/feedback.post.ts @@ -5,12 +5,33 @@ import { } from '../../docs/.vitepress/types/Feedback' export default defineEventHandler(async (event) => { - const { message, page, type } = await readValidatedBody( + const { message, page, type, heading } = await readValidatedBody( event, FeedbackSchema.parseAsync ) const env = useRuntimeConfig(event) + const fields = [ + { + name: 'Page', + value: page, + inline: true + }, + { + name: 'Message', + value: message, + inline: false + } + ] + + if (heading) { + fields.push({ + name: 'Section', + value: heading, + inline: true + }) + } + // FIXME: somehow this is not working, but it worked before // const path = 'feedback' // @@ -21,9 +42,6 @@ export default defineEventHandler(async (event) => { // }) // } - let description = `${message}\n\n` - if (page) description += `**Page:** \`${page}\`` - await fetcher() .post(env.WEBHOOK_URL, { username: 'Feedback', @@ -33,7 +51,7 @@ export default defineEventHandler(async (event) => { { color: 3447003, title: getFeedbackOption(type).label, - description + fields } ] }) diff --git a/api/tsconfig.json b/api/tsconfig.json new file mode 100644 index 000000000..e7468d88c --- /dev/null +++ b/api/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../.nitro/types/tsconfig.json" +} diff --git a/biome.json b/biome.json index 1b2f3c378..791dc426c 100644 --- a/biome.json +++ b/biome.json @@ -1,8 +1,12 @@ { "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", - "extends": ["@taskylizard/biome-config"], + "extends": ["@taskylizard/biome-config", "./docs/.vitepress/.imports.json"], "files": { - "ignore": ["docs/.vitepress/**/*.vue", "docs/.vitepress/vue-shim.d.ts"] + "ignore": [ + "docs/.vitepress/**/*.vue", + "docs/.vitepress/vue-shim.d.ts", + "docs/.vitepress/imports.d.ts" + ] }, "formatter": { "enabled": false @@ -19,12 +23,7 @@ "linter": { "rules": { "style": { - "useFilenamingConvention": { - "level": "info", - "options": { - "filenameCases": ["camelCase", "PascalCase"] - } - }, + "useFilenamingConvention": "off", "noDefaultExport": "off" } } diff --git a/docs/.vitepress/.imports.json b/docs/.vitepress/.imports.json new file mode 100644 index 000000000..a46347555 --- /dev/null +++ b/docs/.vitepress/.imports.json @@ -0,0 +1,73 @@ +{ + "javascript": { + "globals": [ + "Component", + "ComponentPublicInstance", + "ComputedRef", + "EffectScope", + "ExtractDefaultPropTypes", + "ExtractPropTypes", + "ExtractPublicPropTypes", + "InjectionKey", + "PropType", + "Ref", + "VNode", + "WritableComputedRef", + "computed", + "createApp", + "customRef", + "defineAsyncComponent", + "defineComponent", + "effectScope", + "getCurrentInstance", + "getCurrentScope", + "h", + "inject", + "isProxy", + "isReactive", + "isReadonly", + "isRef", + "markRaw", + "nextTick", + "onActivated", + "onBeforeMount", + "onBeforeUnmount", + "onBeforeUpdate", + "onDeactivated", + "onErrorCaptured", + "onMounted", + "onRenderTracked", + "onRenderTriggered", + "onScopeDispose", + "onServerPrefetch", + "onUnmounted", + "onUpdated", + "provide", + "reactive", + "readonly", + "ref", + "resolveComponent", + "shallowReactive", + "shallowReadonly", + "shallowRef", + "toRaw", + "toRef", + "toRefs", + "toValue", + "triggerRef", + "unref", + "useAttrs", + "useCssModule", + "useCssVars", + "useData", + "useRoute", + "useRouter", + "useSlots", + "watch", + "watchEffect", + "watchPostEffect", + "watchSyncEffect", + "withBase" + ] + } +} \ No newline at end of file diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 449f3338c..6987787ae 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -12,7 +12,9 @@ import { import { generateFeed, generateImages, generateMeta } from './hooks' import { defs, emojiRender, movePlugin } from './markdown/emoji' import { toggleStarredPlugin } from './markdown/toggleStarred' +import { headersPlugin } from './markdown/headers' import { transforms } from './transformer' +import AutoImport from 'unplugin-auto-import/vite' // @unocss-include @@ -56,6 +58,15 @@ export default defineConfig({ UnoCSS({ configFile: '../unocss.config.ts' }), + AutoImport({ + dts: './.vitepress/imports.d.ts', + imports: ['vue', 'vitepress'], + vueTemplate: true, + biomelintrc: { + enabled: true, + filepath: './docs/.vitepress/.imports.json' + } + }), transforms(), { name: 'custom:adjust-order', @@ -85,6 +96,7 @@ export default defineConfig({ config(md) { md.use(emojiRender) md.use(toggleStarredPlugin) + md.use(headersPlugin) } }, themeConfig: { diff --git a/docs/.vitepress/hooks/opengraph.ts b/docs/.vitepress/hooks/opengraph.ts index 74b62c505..79e486a69 100644 --- a/docs/.vitepress/hooks/opengraph.ts +++ b/docs/.vitepress/hooks/opengraph.ts @@ -71,14 +71,14 @@ async function generateImage({ const _page = getPage(url) const title = frontmatter.layout === 'home' - ? frontmatter.hero.name ?? frontmatter.title + ? (frontmatter.hero.name ?? frontmatter.title) : frontmatter.title ? frontmatter.title : _page?.title const description = frontmatter.layout === 'home' - ? frontmatter.hero.tagline ?? frontmatter.description + ? (frontmatter.hero.tagline ?? frontmatter.description) : frontmatter.description ? frontmatter.description : _page?.description diff --git a/docs/.vitepress/imports.d.ts b/docs/.vitepress/imports.d.ts new file mode 100644 index 000000000..3bc9dfa9b --- /dev/null +++ b/docs/.vitepress/imports.d.ts @@ -0,0 +1,133 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +export {} +declare global { + const EffectScope: typeof import('vue')['EffectScope'] + const computed: typeof import('vue')['computed'] + const createApp: typeof import('vue')['createApp'] + const customRef: typeof import('vue')['customRef'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const effectScope: typeof import('vue')['effectScope'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const inject: typeof import('vue')['inject'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const provide: typeof import('vue')['provide'] + const reactive: typeof import('vue')['reactive'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const toRaw: typeof import('vue')['toRaw'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const toValue: typeof import('vue')['toValue'] + const triggerRef: typeof import('vue')['triggerRef'] + const unref: typeof import('vue')['unref'] + const useAttrs: typeof import('vue')['useAttrs'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVars: typeof import('vue')['useCssVars'] + const useData: typeof import('vitepress')['useData'] + const useRoute: typeof import('vitepress')['useRoute'] + const useRouter: typeof import('vitepress')['useRouter'] + const useSlots: typeof import('vue')['useSlots'] + const watch: typeof import('vue')['watch'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] + const withBase: typeof import('vitepress')['withBase'] +} +// for type re-export +declare global { + // @ts-ignore + export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' + import('vue') +} +// for vue template auto import +import { UnwrapRef } from 'vue' +declare module 'vue' { + interface GlobalComponents {} + interface ComponentCustomProperties { + readonly EffectScope: UnwrapRef + readonly computed: UnwrapRef + readonly createApp: UnwrapRef + readonly customRef: UnwrapRef + readonly defineAsyncComponent: UnwrapRef + readonly defineComponent: UnwrapRef + readonly effectScope: UnwrapRef + readonly getCurrentInstance: UnwrapRef + readonly getCurrentScope: UnwrapRef + readonly h: UnwrapRef + readonly inject: UnwrapRef + readonly isProxy: UnwrapRef + readonly isReactive: UnwrapRef + readonly isReadonly: UnwrapRef + readonly isRef: UnwrapRef + readonly markRaw: UnwrapRef + readonly nextTick: UnwrapRef + readonly onActivated: UnwrapRef + readonly onBeforeMount: UnwrapRef + readonly onBeforeUnmount: UnwrapRef + readonly onBeforeUpdate: UnwrapRef + readonly onDeactivated: UnwrapRef + readonly onErrorCaptured: UnwrapRef + readonly onMounted: UnwrapRef + readonly onRenderTracked: UnwrapRef + readonly onRenderTriggered: UnwrapRef + readonly onScopeDispose: UnwrapRef + readonly onServerPrefetch: UnwrapRef + readonly onUnmounted: UnwrapRef + readonly onUpdated: UnwrapRef + readonly provide: UnwrapRef + readonly reactive: UnwrapRef + readonly readonly: UnwrapRef + readonly ref: UnwrapRef + readonly resolveComponent: UnwrapRef + readonly shallowReactive: UnwrapRef + readonly shallowReadonly: UnwrapRef + readonly shallowRef: UnwrapRef + readonly toRaw: UnwrapRef + readonly toRef: UnwrapRef + readonly toRefs: UnwrapRef + readonly toValue: UnwrapRef + readonly triggerRef: UnwrapRef + readonly unref: UnwrapRef + readonly useAttrs: UnwrapRef + readonly useCssModule: UnwrapRef + readonly useCssVars: UnwrapRef + readonly useData: UnwrapRef + readonly useRoute: UnwrapRef + readonly useRouter: UnwrapRef + readonly useSlots: UnwrapRef + readonly watch: UnwrapRef + readonly watchEffect: UnwrapRef + readonly watchPostEffect: UnwrapRef + readonly watchSyncEffect: UnwrapRef + readonly withBase: UnwrapRef + } +} diff --git a/docs/.vitepress/markdown/headers.ts b/docs/.vitepress/markdown/headers.ts new file mode 100644 index 000000000..31ffe238b --- /dev/null +++ b/docs/.vitepress/markdown/headers.ts @@ -0,0 +1,16 @@ +import type { MarkdownRenderer } from 'vitepress' +import { headers } from '../transformer/constants' + +const titles = Object.keys(headers).map((key) => headers[key].title) + +export const headersPlugin = (md: MarkdownRenderer) => { + // Add the Feedback component after the heading and close the container + md.renderer.rules.heading_close = (tokens, idx, options, env, self) => { + const result = self.renderToken(tokens, idx, options) + const heading = tokens[idx - 1] + const level = tokens[idx].tag.slice(1) + if (!titles.includes(env.frontmatter.title) || level !== '2') return result + + return `${result}` + } +} diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue index ada010bfb..5401d9602 100644 --- a/docs/.vitepress/theme/Layout.vue +++ b/docs/.vitepress/theme/Layout.vue @@ -1,7 +1,5 @@ @@ -8,7 +6,7 @@ const { frontmatter } = useData() {{ frontmatter.hero.prelink.title }} diff --git a/docs/.vitepress/theme/components/Authors.vue b/docs/.vitepress/theme/components/Authors.vue index 36f7dd1f8..560f43e24 100644 --- a/docs/.vitepress/theme/components/Authors.vue +++ b/docs/.vitepress/theme/components/Authors.vue @@ -40,8 +40,8 @@ const authors = computed(() =>