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
3 changes: 3 additions & 0 deletions packages/client/composables/useNav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { slides } from '#slidev/slides'
import { clamp } from '@antfu/utils'
import { parseRangeString } from '@slidev/parser/utils'
import { createSharedComposable } from '@vueuse/core'
import { hideAllPoppers } from 'floating-vue'
import { computed, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { CLICKS_MAX } from '../constants'
Expand Down Expand Up @@ -118,6 +119,8 @@ export function useNavBase(

watch(currentSlideRoute, (next, prev) => {
navDirection.value = next.no - prev.no
if (prev)
hideAllPoppers()
})

async function openInEditor(url?: string) {
Expand Down
1 change: 1 addition & 0 deletions packages/slidev/node/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ function getDefine(options: Omit<ResolvedSlidevOptions, 'utils'>): Record<string
__DEV__: options.mode === 'dev',
__SLIDEV_CLIENT_ROOT__: toAtFS(options.clientRoot),
__SLIDEV_HASH_ROUTE__: options.data.config.routerMode === 'hash',
__SLIDEV_FEATURE_TWOSLASH_AUTOSHOW__: false,
__SLIDEV_FEATURE_DRAWINGS__: matchMode(options.data.config.drawings.enabled),
__SLIDEV_FEATURE_EDITOR__: options.mode === 'dev' && options.data.config.editor !== false,
__SLIDEV_FEATURE_DRAWINGS_PERSIST__: !!options.data.config.drawings.persist,
Expand Down
3 changes: 3 additions & 0 deletions packages/slidev/node/syntax/markdown-it/markdown-it-shiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ResolvedSlidevOptions } from '@slidev/types'
import type { ShikiTransformer } from 'shiki'
import { isTruthy } from '@antfu/utils'
import { fromHighlighter } from '@shikijs/markdown-it/core'
import { transformerTwoslashConditional } from '../transform/twoslash-conditional'
import { escapeVueInCode } from '../transform/utils'

export default async function MarkdownItShiki({ data: { config }, mode, utils }: ResolvedSlidevOptions) {
Expand All @@ -16,6 +17,8 @@ export default async function MarkdownItShiki({ data: { config }, mode, utils }:
},
},
}),
(config.twoslash === true || config.twoslash === mode)
&& transformerTwoslashConditional(),
{
pre(pre) {
this.addClassToHast(pre, 'slidev-code')
Expand Down
33 changes: 33 additions & 0 deletions packages/slidev/node/syntax/transform/twoslash-conditional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Element } from 'hast'
import type { ShikiTransformer } from 'shiki'

const FLAG_NAME = '__SLIDEV_FEATURE_TWOSLASH_AUTOSHOW__'

function applyAutoShowFlag(node: Element | undefined) {
if (!node)
return

if (node.tagName === 'v-menu') {
const properties = node.properties || (node.properties = {})
if (properties[':shown'] === 'true')
properties[':shown'] = FLAG_NAME
}

const children = Array.isArray(node.children) ? node.children : []
for (const child of children) {
if (child && typeof child === 'object' && 'type' in child && child.type === 'element')
applyAutoShowFlag(child as Element)
}
}

/**
* Custom Twoslash transformer that rewrites the shown binding to use the build-time flag.
*/
export function transformerTwoslashConditional(): ShikiTransformer {
Copy link
Member

@antfu antfu Oct 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use the build-time flag rather than a custom codemod (which is unstable), like:

function getDefine(options: Omit<ResolvedSlidevOptions, 'utils'>): Record<string, string> {
const matchMode = (mode: string | boolean) => mode === true || mode === options.mode
return objectMap(
{
__DEV__: options.mode === 'dev',
__SLIDEV_CLIENT_ROOT__: toAtFS(options.clientRoot),
__SLIDEV_HASH_ROUTE__: options.data.config.routerMode === 'hash',
__SLIDEV_FEATURE_DRAWINGS__: matchMode(options.data.config.drawings.enabled),
__SLIDEV_FEATURE_EDITOR__: options.mode === 'dev' && options.data.config.editor !== false,
__SLIDEV_FEATURE_DRAWINGS_PERSIST__: !!options.data.config.drawings.persist,
__SLIDEV_FEATURE_RECORD__: matchMode(options.data.config.record),
__SLIDEV_FEATURE_PRESENTER__: matchMode(options.data.config.presenter),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for your review!

summarize my thoughts and questions:

  • I don’t think it needs to be a configurable option. If it is not set to false, these popups will appear on pages where they don’t belong.
  • Walking the Shiki-generated HAST tree and then modifying the tree (as in my latest commits). This seems better than doing string replacements — do you think this approach is stable enough?
  • I’m not sure I got your point about the "build-time flag" - could you please clarify what this flag is supposed to do in your mind?

return {
name: 'slidev:twoslash-conditional',
code(codeEl) {
applyAutoShowFlag(codeEl as Element)
},
}
}
2 changes: 2 additions & 0 deletions shim.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ declare global {
const __DEV__: boolean
const __SLIDEV_HASH_ROUTE__: boolean
const __SLIDEV_CLIENT_ROOT__: string
const __SLIDEV_FEATURE_TWOSLASH_AUTOSHOW__: boolean
const __SLIDEV_FEATURE_DRAWINGS__: boolean
const __SLIDEV_FEATURE_DRAWINGS_PERSIST__: boolean
const __SLIDEV_FEATURE_EDITOR__: boolean
Expand All @@ -19,6 +20,7 @@ declare module '@vue/runtime-core' {
__DEV__: boolean
__SLIDEV_HASH_ROUTE__: boolean
__SLIDEV_CLIENT_ROOT__: string
__SLIDEV_FEATURE_TWOSLASH_AUTOSHOW__: boolean
__SLIDEV_FEATURE_DRAWINGS__: boolean
__SLIDEV_FEATURE_DRAWINGS_PERSIST__: boolean
__SLIDEV_FEATURE_EDITOR__: boolean
Expand Down
Loading