Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions app/assets/css/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,20 @@ html.dark .shiki span {
.mermaid {
@apply rounded overflow-hidden border border-muted bg-white dark:bg-muted;
}

/*
* Heading anchor icon (`lucide:hash`) for headings rendered as plain elements by the `elements`
* comark plugin (`server/utils/comark-elements.ts`). Replaces `<UIcon name="lucide:hash">`,
* whose CSS `@nuxt/icon` only emits for rendered components. Same mask technique as iconify.
*/
.prose-anchor-icon {
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 9h16M4 15h16M10 3L8 21M16 3l-2 21'/%3E%3C/svg%3E");
display: inline-block;
background-color: currentColor;
-webkit-mask-image: var(--svg);
mask-image: var(--svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
}
2 changes: 1 addition & 1 deletion app/pages/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@

const surroundLinks = computed(() => findSurroundLinks(navigation?.value, selfPath.value))

const fm = computed<Record<string, any>>(() => page.value?.data ?? {})

Check warning on line 39 in app/pages/[...slug].vue

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
const tocLinks = computed<any[]>(() => (page.value?.meta as any)?.toc?.links ?? [])

Check warning on line 40 in app/pages/[...slug].vue

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

Check warning on line 40 in app/pages/[...slug].vue

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

const title = computed(() => fm.value.seo?.title || fm.value.title)
const description = computed(() => fm.value.seo?.description || fm.value.description)
Expand Down Expand Up @@ -122,7 +122,7 @@
<MarkdownDocument
v-if="tree"
:value="tree"
:components="{ Mermaid, CodeExplorer, Browser }"
:components="{ ...proseElements, Mermaid, CodeExplorer, Browser }"
/>

<UContentSurround
Expand Down
1 change: 1 addition & 0 deletions app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import LandingHeroDemo from '../components/landing/LandingHeroDemo.vue'

const landingComponents = {
...proseElements,
LandingStack,
LandingTabs,
LandingFeatures,
Expand All @@ -31,7 +32,7 @@
return p ? { ...p, nodes: prefixTreeLinks(p.nodes, content.value.base) } : p
})

const fm = computed<Record<string, any>>(() => page.value?.data ?? {})

Check warning on line 35 in app/pages/index.vue

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

// Keep branch/commit previews out of search, same as DocsPage.
useRobotsRule(computed(() => (content.value.mode === 'prod' ? 'index, follow' : 'noindex, nofollow')))
Expand Down
45 changes: 45 additions & 0 deletions app/utils/prose-elements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { defineAsyncComponent, hydrateOnVisible } from 'vue'

/**
* `ProseImg` keeps its component (zoom dialog), but hydrates lazily: the chunk only downloads
* and hydrates once an image scrolls into the viewport.
*/
const LazyProseImg = defineAsyncComponent({
loader: () => import('@nuxt/ui/runtime/components/prose/Img.vue'),
hydrate: hydrateOnVisible(),
})

/**
* Component map for `<MarkdownDocument :components>` covering every tag the `elements` comark
* plugin (`server/utils/comark-elements.ts`) rewrites to final HTML with baked-in classes.
*
* Mapping a tag to its own name short-circuits the renderer's `Prose*` lookup, so it emits a
* plain element (`h('p')`) instead of mounting the global Nuxt UI component — removing per-node
* component instances from SSR, hydration, and client-side navigation. Children still render
* normally, so links and inline components inside these elements stay interactive.
*/
export const proseElements = {
p: 'p',
em: 'em',
strong: 'strong',
hr: 'hr',
ul: 'ul',
ol: 'ol',
li: 'li',
blockquote: 'blockquote',
code: 'code',
table: 'table',
thead: 'thead',
tbody: 'tbody',
tr: 'tr',
th: 'th',
td: 'td',
h1: 'h1',
h2: 'h2',
h3: 'h3',
h4: 'h4',
// Heading anchor links emitted by the plugin: rendered as plain `<a>` (a bare `a` tag would
// resolve to the global `ProseA` and pick up content-link styling).
'prose-anchor': 'a',
img: LazyProseImg,
}
3 changes: 3 additions & 0 deletions modules/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export default defineNuxtModule({

const sources = [
resolver.resolve('../app'),
// The `elements` comark plugin bakes prose classes into parsed trees at runtime; scanning
// `server/` keeps those literals covered even if they drift from the Nuxt UI prose theme.
resolver.resolve('../server'),
nuxt.options.srcDir,
resolve(nuxt.options.rootDir, 'content'),
].map((dir) => `${dir}/**/*`.replace(/\\/g, '/'))
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"pathe": "^2.0.3",
"rangi": "^2.2.0",
"satori": "^0.29.1",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.3.3",
"ufo": "^1.6.4",
"unstorage": "^1.17.5",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

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

2 changes: 1 addition & 1 deletion server/utils/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function cacheAvailable(): boolean {
* Bump when content parser/plugin configuration, relevant parser dependencies, or cached derived
* data changes. Keeping this explicit lets unrelated deployments reuse immutable content artifacts.
*/
export const CONTENT_PARSER_VERSION = 'v2'
export const CONTENT_PARSER_VERSION = 'v3'

/** Per-parser-version, per-content-SHA driver backing comark's manifest and parsed bodies. */
export function cacheDriver(sha: string): Driver {
Expand Down
Loading
Loading