Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c08226e
feat: new components `<CodeTabs>` and `<CodeTabPanel>`
phonzammi Oct 25, 2025
6ee35c1
feat: new `remarkJsxVueTabs` plugin
phonzammi Oct 28, 2025
39b373b
minor
phonzammi Oct 28, 2025
76e7baa
comments
phonzammi Oct 28, 2025
097bfd1
add example
phonzammi Oct 28, 2025
0a53fec
Merge branch 'main' of https://github.com/brillout/docpress into phon…
phonzammi Oct 29, 2025
3b61367
remove `remarkJsxVueTabs`
phonzammi Nov 6, 2025
598e125
minor
phonzammi Nov 6, 2025
dbde51c
feat: `remarkCodeTabs`
phonzammi Nov 6, 2025
5a9edef
add example
phonzammi Nov 6, 2025
d2703e7
Removed/Added file using `file-added`/`file-removed` meta
phonzammi Nov 6, 2025
2ae2e8d
refactor: group code nodes by language, then by choice
phonzammi Nov 16, 2025
0c987ea
feat: `remarkPkgManager`
phonzammi Nov 22, 2025
bd946a5
minor
phonzammi Nov 22, 2025
9aa95d9
refactor
phonzammi Nov 22, 2025
5d0625b
remove Code Tabs
phonzammi Jan 4, 2026
043da1e
feat: CodeGroup
phonzammi Jan 4, 2026
75657c0
Merge branch 'main' of https://github.com/brillout/docpress into phon…
phonzammi Jan 4, 2026
98807c2
format
phonzammi Jan 4, 2026
5a0b260
Merge branch 'main' of https://github.com/brillout/docpress into phon…
phonzammi Jan 7, 2026
ad9630b
Merge branch 'main' of https://github.com/brillout/docpress into phon…
phonzammi Jan 11, 2026
7ade261
refactor: use new config `+docpress.choices`
phonzammi Jan 12, 2026
853bc78
refactor
phonzammi Jan 13, 2026
1e64356
reproduce bug
brillout Jan 13, 2026
59428d5
make sure to skip single choice for now
phonzammi Jan 14, 2026
846cade
pass and use `choices: string[]` instead of using `children` to find …
phonzammi Jan 14, 2026
f2f6537
rename `data-key` to `data-group-name`
phonzammi Jan 14, 2026
a58173a
support single choice
phonzammi Jan 15, 2026
85b3514
fix fouc
phonzammi Jan 15, 2026
8e1f864
add `try-catch` and `assertWarning` for `useLocalStorage()`
phonzammi Jan 15, 2026
f837e53
dedupe code in `useSelectCodeLang.ts`
phonzammi Jan 15, 2026
dc22182
comment
phonzammi Jan 15, 2026
9f1bb9f
dedupe code in `CodeSnippets.tsx`
phonzammi Jan 15, 2026
c64df8b
Revert "add `try-catch` and `assertWarning` for `useLocalStorage()`"
phonzammi Jan 15, 2026
d852f82
support parial choices
phonzammi Jan 16, 2026
509c2b5
example of partial choices
phonzammi Jan 16, 2026
f608f7b
keep using `<select>` when single choice
phonzammi Jan 16, 2026
ab3c1a4
rename_full CodeGroup ChoiceGroup
phonzammi Jan 16, 2026
fc2ed19
add tests
phonzammi Jan 17, 2026
c212cfc
minor
phonzammi Jan 17, 2026
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
14 changes: 14 additions & 0 deletions demo/+docpress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ const config: Config = {
// globalNote: <GlobalNoteWarning />,
topNavigation: <TopNavigation />,
navMaxWidth: 1140,
choices: {
'pkg-manager': {
choices: ['npm', 'pnpm', 'yarn', 'bun'],
default: 'npm',
},
server: {
choices: ['hono', 'express', 'fastify'],
default: 'hono',
},
'ui-ext': {
choices: ['vike-react', 'vike-vue', 'vike-solid'],
default: 'vike-react',
},
},
}

/*
Expand Down
146 changes: 146 additions & 0 deletions demo/pages/features/+Page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,149 @@ async function onNewTodo(text: string) {
// https://vike.dev
const a: number = 1
```

## ChoiceGroup

Using `choice=choice-name` meta to group code blocks into multiple choices:

```shell choice=hono
npm i hono @photonjs/hono
```

```shell choice=express
npm i express @photonjs/express
```

```ts choice=hono file-added
// server/index.ts

import { Hono } from 'hono'
import { apply, serve } from 'vike-photon/hono'

function startServer() {
const app = new Hono()
apply(app)
return serve(app)
}

export default startServer()
```

```ts choice=express file-added
// server/index.ts

import express from 'express'
import { apply, serve } from 'vike-photon/express'

function startServer() {
const app = express()
apply(app)
return serve(app)
}

export default startServer()
```

Skipping the first choice from `+docpress.choices['server']` caused it to become the third disabled option in the select options.

```ts choice=express file-added
// server/index.ts

import express from 'express'
import { apply, serve } from 'vike-photon/express'

function startServer() {
const app = express()
apply(app)
return serve(app)
}

export default startServer()
```

```ts choice=fastify file-added
// server/index.ts

import fastify from 'fastify'
import rawBody from 'fastify-raw-body'
import { apply, serve } from '@photonjs/fastify'

async function startServer() {
const app = fastify({
// ⚠️ Mandatory for HMR support
forceCloseConnections: true
})
// ⚠️ Mandatory for Vike's SSR middleware
await app.register(rawBody)
await apply(app)
return serve(app)
}

export default startServer()
```

Some paragraph.

:::Choice{#express}
> Express.js is deprecated.
:::


Using `:::Choice{#choice-name} {:mdx}` directive to combine multiple contents for a single choice:

:::Choice{#vike-react}
```tsx
import { ClientOnly } from 'vike-react/ClientOnly'

function Page() {
return (
<ClientOnly fallback={<p>Loading...</p>}>
<SomeComponent />
</ClientOnly>
)
}
```

Props:
- **children**: Content rendered only on the client-side after hydration.
- **fallback** (optional): Content shown during SSR and before hydration completes.
:::

:::Choice{#vike-vue}
```vue
<template>
<ClientOnly>
<SomeComponent />
<template #fallback>
<p>Loading...</p>
</template>
</ClientOnly>
</template>

<script setup lang="ts">
import { ClientOnly } from 'vike-vue/ClientOnly'
</script>
```

Props:
- **default slot**: Content rendered only on the client-side after hydration.
- **fallback slot** (optional): Content shown during SSR and before hydration completes.
:::

:::Choice{#vike-solid}
```tsx
import { ClientOnly } from 'vike-solid/ClientOnly'

function Page() {
return (
<ClientOnly fallback={<p>Loading...</p>}>
<SomeComponent />
</ClientOnly>
)
}
```

Props:
- **children**: Content rendered only on the client-side after hydration.
- **fallback** (optional): Content shown during SSR and before hydration completes.
:::
86 changes: 86 additions & 0 deletions demo/testRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,92 @@ function testRun(cmd: 'pnpm run dev' | 'pnpm run preview') {
}
})

test(`${featuresURL} - Choice Group`, async () => {
const firstChoiceText1 = 'npm i hono @photonjs/hono'
const firstChoiceText2 = "import { Hono } from 'hono'"
const secondChoiceText1 = 'pnpm add express @photonjs/express'
const secondChoiceText2 = "import express from 'express'"
const fastifyChoiceText = "import fastify from 'fastify'"

const hasFirstChoice = (text: string | null, yes = true) => {
expect(text).not.toBe(null)
if (yes) {
expect(text).toContain(firstChoiceText1)
expect(text).toContain(firstChoiceText2)
} else {
expect(text).not.toContain(firstChoiceText1)
expect(text).not.toContain(firstChoiceText2)
}
}

const hasSecondChoice = (text: string | null, yes = true) => {
expect(text).not.toBe(null)
if (yes) {
expect(text).toContain(secondChoiceText1)
expect(text).toContain(secondChoiceText2)
} else {
expect(text).not.toContain(secondChoiceText1)
expect(text).not.toContain(secondChoiceText2)
}
}

const hasFastifyChoice = (text: string | null, yes = true) => {
expect(text).not.toBe(null)
if (yes) {
expect(text).toContain(fastifyChoiceText)
} else {
expect(text).not.toContain(fastifyChoiceText)
}
}

const textFull = await page.textContent('body')

hasFirstChoice(textFull)
hasSecondChoice(textFull)
hasFastifyChoice(textFull)

const expectFirstChoice = async () => {
const text = await getVisibleText(page)
hasFirstChoice(text)
hasSecondChoice(text, false)
hasFastifyChoice(text, false)
}

const expectSecondChoice = async () => {
const text = await getVisibleText(page)
hasFirstChoice(text, false)
hasSecondChoice(text)
hasFastifyChoice(text, false)
}

const expectFastifyChoice = async () => {
await page.locator('select[name="server-choices"]:visible').nth(2).selectOption('fastify')
const text = await getVisibleText(page)
hasFirstChoice(text, false)
hasSecondChoice(text, false)
hasFastifyChoice(text)
}

await page.evaluate(() => window.localStorage.clear())

await expectFirstChoice()

await page.selectOption(`select[name="pkg-manager-choices"]:visible`, { index: isDev ? 0 : 1 })
await page.selectOption(`select[name="server-choices"]:visible`, { index: isDev ? 0 : 1 })

await autoRetry(
async () => {
if (isDev) {
await expectFirstChoice()
} else {
await expectSecondChoice()
await expectFastifyChoice()
}
},
{ timeout: 5 * 1000 },
)
})

const somePageUrl = '/some-page'
test(`${somePageUrl} - custom <Pre> injected into nested MDX`, async () => {
await page.goto(getServerUrl() + somePageUrl)
Expand Down
57 changes: 57 additions & 0 deletions pnpm-lock.yaml

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

Loading