From 0b1491c30e1a99c5b0888a6689b6f50c8feb834b Mon Sep 17 00:00:00 2001 From: Ronald Urbina <140639086+ronald-urbina@users.noreply.github.com> Date: Thu, 18 Jun 2026 12:07:23 -0300 Subject: [PATCH] docs(site): add "No Skin" option to installation skin picker (#1525) --- packages/cli/src/commands/docs.ts | 2 +- packages/cli/src/commands/tests/docs.test.ts | 8 ++ packages/cli/src/site-modules.d.ts | 2 +- packages/cli/src/utils/prompts.ts | 4 +- packages/cli/src/utils/tests/format.test.ts | 7 + packages/html/src/cdn/audio-headless.ts | 1 + packages/html/src/cdn/video-headless.ts | 1 + packages/html/tsdown.cdn.config.ts | 2 + .../components/installation/SkinPicker.tsx | 4 +- site/src/content/docs/how-to/installation.mdx | 2 +- .../installation/__tests__/cdn-code.test.ts | 12 ++ .../installation/__tests__/codegen.test.ts | 17 +++ site/src/utils/installation/cdn-code.ts | 1 + site/src/utils/installation/codegen.ts | 125 ++++++++++++------ site/src/utils/installation/types.ts | 2 +- 15 files changed, 146 insertions(+), 44 deletions(-) create mode 100644 packages/html/src/cdn/audio-headless.ts create mode 100644 packages/html/src/cdn/video-headless.ts diff --git a/packages/cli/src/commands/docs.ts b/packages/cli/src/commands/docs.ts index f7d32ff4..2eedc04a 100644 --- a/packages/cli/src/commands/docs.ts +++ b/packages/cli/src/commands/docs.ts @@ -106,7 +106,7 @@ const DOCS_HELP = `Usage: @videojs/cli docs [--framework ] Installation flags (for docs how-to/installation): --preset - --skin + --skin --source-url --media --install-method `; diff --git a/packages/cli/src/commands/tests/docs.test.ts b/packages/cli/src/commands/tests/docs.test.ts index fe2492c2..aed9beb1 100644 --- a/packages/cli/src/commands/tests/docs.test.ts +++ b/packages/cli/src/commands/tests/docs.test.ts @@ -241,6 +241,14 @@ describe('handleDocs', () => { expect(output()).toContain('minimal'); }); + it('generates headless (no skin) variant with skin none', async () => { + await handleDocs(htmlFlags({ skin: 'none' }), ['how-to/installation']); + const out = output(); + expect(out).toContain(''); + expect(out).not.toContain(''); + expect(out).not.toContain("'@videojs/html/video/skin'"); + }); + it('includes custom source URL in generated code', async () => { await handleDocs(htmlFlags({ 'source-url': 'https://example.com/my-video.mp4' }), ['how-to/installation']); expect(output()).toContain('https://example.com/my-video.mp4'); diff --git a/packages/cli/src/site-modules.d.ts b/packages/cli/src/site-modules.d.ts index fd4462c7..0f644faf 100644 --- a/packages/cli/src/site-modules.d.ts +++ b/packages/cli/src/site-modules.d.ts @@ -8,7 +8,7 @@ declare module '@/utils/installation/types' { export type Renderer = 'background-video' | 'hls' | 'html5-audio' | 'html5-video'; - export type Skin = 'video' | 'audio' | 'minimal-video' | 'minimal-audio'; + export type Skin = 'video' | 'audio' | 'minimal-video' | 'minimal-audio' | 'none'; export type UseCase = 'default-video' | 'default-audio' | 'background-video'; export type InstallMethod = 'cdn' | 'npm' | 'pnpm' | 'yarn' | 'bun'; export const VALID_RENDERERS: Record; diff --git a/packages/cli/src/utils/prompts.ts b/packages/cli/src/utils/prompts.ts index 4821eb8c..ca9d1b59 100644 --- a/packages/cli/src/utils/prompts.ts +++ b/packages/cli/src/utils/prompts.ts @@ -46,6 +46,7 @@ function skinOptionsForUseCase(useCase: UseCase): Array<{ value: Skin; label: st return [ { value: isAudio ? 'audio' : 'video', label: 'Default' }, { value: isAudio ? 'minimal-audio' : 'minimal-video', label: 'Minimal' }, + { value: 'none', label: 'None (headless)' }, ]; } @@ -76,10 +77,11 @@ export function mapRawSkin(skinFlag: string, useCase: UseCase): Skin { const map: Record = { default: isAudio ? 'audio' : 'video', minimal: isAudio ? 'minimal-audio' : 'minimal-video', + none: 'none', }; const result = map[skinFlag]; if (!result) { - console.error(`Invalid skin: "${skinFlag}". Must be "default" or "minimal".`); + console.error(`Invalid skin: "${skinFlag}". Must be "default", "minimal", or "none".`); process.exit(1); } return result; diff --git a/packages/cli/src/utils/tests/format.test.ts b/packages/cli/src/utils/tests/format.test.ts index a985b8ac..7022ac5c 100644 --- a/packages/cli/src/utils/tests/format.test.ts +++ b/packages/cli/src/utils/tests/format.test.ts @@ -51,4 +51,11 @@ describe('formatInstallationCode', () => { const result = formatInstallationCode({ ...baseReact, installMethod: 'pnpm' }); expect(result).toContain('pnpm add @videojs/react'); }); + + it('formats HTML with skin none — omits skin tag and skin import', () => { + const result = formatInstallationCode({ ...baseHTML, skin: 'none' }); + expect(result).toContain(''); + expect(result).not.toContain(''); + expect(result).not.toContain("'@videojs/html/video/skin'"); + }); }); diff --git a/packages/html/src/cdn/audio-headless.ts b/packages/html/src/cdn/audio-headless.ts new file mode 100644 index 00000000..2f2fd6b4 --- /dev/null +++ b/packages/html/src/cdn/audio-headless.ts @@ -0,0 +1 @@ +import '../define/audio/player'; diff --git a/packages/html/src/cdn/video-headless.ts b/packages/html/src/cdn/video-headless.ts new file mode 100644 index 00000000..7d426745 --- /dev/null +++ b/packages/html/src/cdn/video-headless.ts @@ -0,0 +1 @@ +import '../define/video/player'; diff --git a/packages/html/tsdown.cdn.config.ts b/packages/html/tsdown.cdn.config.ts index 5ab1a980..8b06447d 100644 --- a/packages/html/tsdown.cdn.config.ts +++ b/packages/html/tsdown.cdn.config.ts @@ -15,12 +15,14 @@ const buildModes: BuildMode[] = ['dev', 'prod']; const presets = [ 'video', + 'video-headless', 'video-minimal', 'video-ui', 'video-minimal-ui', 'live-video', 'live-video-minimal', 'audio', + 'audio-headless', 'audio-minimal', 'audio-ui', 'audio-minimal-ui', diff --git a/site/src/components/installation/SkinPicker.tsx b/site/src/components/installation/SkinPicker.tsx index 74d3e55b..80797f98 100644 --- a/site/src/components/installation/SkinPicker.tsx +++ b/site/src/components/installation/SkinPicker.tsx @@ -1,5 +1,5 @@ import { useStore } from '@nanostores/react'; -import { Minus, Sparkles } from 'lucide-react'; +import { Code2, Minus, Sparkles } from 'lucide-react'; import { useEffect } from 'react'; import type { ImageRadioOption } from '@/components/ImageRadioGroup'; import ImageRadioGroup from '@/components/ImageRadioGroup'; @@ -9,11 +9,13 @@ import type { Skin } from '@/utils/installation/types'; const VIDEO_SKINS: ImageRadioOption[] = [ { value: 'video', label: 'Default', image: }, { value: 'minimal-video', label: 'Minimal', image: }, + { value: 'none', label: 'No Skin', image: }, ]; const AUDIO_SKINS: ImageRadioOption[] = [ { value: 'audio', label: 'Default', image: }, { value: 'minimal-audio', label: 'Minimal', image: }, + { value: 'none', label: 'No Skin', image: }, ]; export default function SkinPicker() { diff --git a/site/src/content/docs/how-to/installation.mdx b/site/src/content/docs/how-to/installation.mdx index 9602d8ec..05429e1e 100644 --- a/site/src/content/docs/how-to/installation.mdx +++ b/site/src/content/docs/how-to/installation.mdx @@ -42,7 +42,7 @@ If you run it without flags, it will allow you to interactively configure your p npx @videojs/cli docs how-to/installation \ --framework \ --preset \ - --skin \ + --skin \ --media \ --source-url \ --install-method diff --git a/site/src/utils/installation/__tests__/cdn-code.test.ts b/site/src/utils/installation/__tests__/cdn-code.test.ts index 7df9eb9e..edf2cf35 100644 --- a/site/src/utils/installation/__tests__/cdn-code.test.ts +++ b/site/src/utils/installation/__tests__/cdn-code.test.ts @@ -20,4 +20,16 @@ describe('generateCdnCode', () => { `` ); }); + + it('generates headless video CDN tag when skin is none', () => { + expect(generateCdnCode('default-video', 'none', 'html5-video')).toEqual( + `` + ); + }); + + it('generates headless audio CDN tag when skin is none', () => { + expect(generateCdnCode('default-audio', 'none', 'html5-audio')).toEqual( + `` + ); + }); }); diff --git a/site/src/utils/installation/__tests__/codegen.test.ts b/site/src/utils/installation/__tests__/codegen.test.ts index 0b35330a..bb8d23d8 100644 --- a/site/src/utils/installation/__tests__/codegen.test.ts +++ b/site/src/utils/installation/__tests__/codegen.test.ts @@ -139,6 +139,14 @@ describe('generateHTMLUsageCode', () => { expect(result.js).toContain("import '@videojs/html/video/minimal-skin'"); }); + it('omits skin tag and skin import when skin is none', () => { + const result = generateHTMLUsageCode({ ...baseHTML, skin: 'none' }); + expect(result.html).toContain(''); + expect(result.html).not.toContain(''); + expect(result.js).toContain("import '@videojs/html/video/player'"); + expect(result.js).not.toContain("import '@videojs/html/video/skin'"); + }); + it('uses custom source URL when provided', () => { const result = generateHTMLUsageCode({ ...baseHTML, sourceUrl: 'https://example.com/video.mp4' }); expect(result.html).toContain('https://example.com/video.mp4'); @@ -193,6 +201,15 @@ describe('generateReactCreateCode', () => { expect(code).toContain("import '@videojs/react/video/minimal-skin.css'"); }); + it('omits skin component and CSS import when skin is none', () => { + const result = generateReactCreateCode({ ...baseReact, skin: 'none' }); + const code = result['MyPlayer.tsx']; + expect(code).not.toContain('VideoSkin'); + expect(code).not.toContain('skin.css'); + expect(code).toContain('