Skip to content

Commit 4bf4d27

Browse files
committed
feat(docs): show the GitHub video in a video container above the Introduction to Git page
A video route sharing id 5 with the markdown route is composed into the same page, and the markdown route's hierarchyPage puts the video first. The docs smoke now requires route ids to be unique within each route type.
1 parent ce83870 commit 4bf4d27

9 files changed

Lines changed: 128 additions & 10 deletions

File tree

‎README.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,12 +520,22 @@ in with a configured provider. Route `id=11` has no `authorization`, so the **Vi
520520
its video container are always visible. Videos are played only by the video container; markdown pages
521521
do not embed them.
522522

523+
A video route that shares its `id` with a markdown route is shown on the same page. The *Introduction
524+
to Git* page (`id=5`) uses this to show the test video in a video container **above** its markdown:
525+
the version config has a `routes-video` entry with `"id": 5` (no menu entry of its own) and the
526+
markdown route sets a page order that puts the video first:
527+
528+
```json
529+
{ "id": 5, "hierarchyPage": { "video": 0, "md": 1, "source-viewer": 2, "html": 3, "audio": 4 } }
530+
```
531+
523532
Test video (GitHub channel, *A brief introduction to Git for beginners*):
524533

525534
[![A brief introduction to Git for beginners | GitHub](https://img.youtube.com/vi/r8jQ9hVA2qs/hqdefault.jpg)](https://www.youtube.com/watch?v=r8jQ9hVA2qs)
526535

527536
- Watch on YouTube: https://www.youtube.com/watch?v=r8jQ9hVA2qs
528537
- Video route: https://vidigal-code.github.io/git-page-docs/Vidigal-code/git-page-docs/v/0.0.7/?lang=en&menu=en&id=11
538+
- Introduction to Git page (video above the markdown): https://vidigal-code.github.io/git-page-docs/Vidigal-code/git-page-docs/v/0.0.7/?lang=en&menu=en&id=5
529539
- Fullscreen: https://vidigal-code.github.io/git-page-docs/Vidigal-code/git-page-docs/v/0.0.7/?videofull=en&slug=r8jQ9hVA2qs
530540

531541
Route definition in `gitpagedocs/docs/versions/0.0.7/config.json`:

‎cli/builders/route-builders.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export function buildMdRoute(versionId, routeId, pathByLang, titles, description
2424
RouteguideBrandContainerTop = false,
2525
audio,
2626
authorization,
27+
hierarchyPage,
2728
} = options;
2829
const out = {
2930
id: routeId,
@@ -54,6 +55,7 @@ export function buildMdRoute(versionId, routeId, pathByLang, titles, description
5455
if (container !== undefined) out.container = container;
5556
if (audio !== undefined) out.audio = audio;
5657
if (authorization !== undefined) out.authorization = authorization;
58+
if (hierarchyPage !== undefined) out.hierarchyPage = hierarchyPage;
5759
return out;
5860
}
5961

‎cli/builders/version-config-builder.mjs‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
AUDIO_META_ID12,
2121
SOURCE_VIEWER_META,
2222
DEFAULT_HIERARCHY,
23+
VIDEO_FIRST_HIERARCHY,
2324
} from "../data/route-metas.mjs";
2425
import { buildAudioRoute, buildMdRoute, buildSourceViewerRoute, buildVideoRoute } from "./route-builders.mjs";
2526
import { resolveSourceViewerPath } from "./project-links.mjs";
@@ -39,6 +40,10 @@ const VIDEO_ROUTE_AUTHORIZATION = {
3940
const PUBLIC_VIDEO_ROUTE_ID = 11;
4041
/** Video routes follow the six markdown pages and the source viewer (ids 8-11). */
4142
const VIDEO_ROUTE_ID_OFFSET = 7;
43+
/** Markdown page that also shows the public video, in a video container above its text. */
44+
const VIDEO_PAGE_ROUTE_ID = 5;
45+
/** VIDEO_IDS / VIDEO_METAS entry of the public video (GitHub's introduction to Git). */
46+
const PUBLIC_VIDEO_INDEX = PUBLIC_VIDEO_ROUTE_ID - VIDEO_ROUTE_ID_OFFSET;
4247

4348
const ROUTE_METAS = {
4449
1: ROUTE_META_ID1,
@@ -62,6 +67,7 @@ function buildVersionMdRoutes(versionId, includeDemoAuthorization) {
6267
};
6368
const routeOptions = {
6469
...(id === 2 ? { audio: PAGE2_AUDIO } : {}),
70+
...(id === VIDEO_PAGE_ROUTE_ID ? { hierarchyPage: VIDEO_FIRST_HIERARCHY } : {}),
6571
...(includeDemoAuthorization && id === 3
6672
? { authorization: { requiredRoles: [MAINTAINER_ROLE] } }
6773
: {}),
@@ -80,8 +86,20 @@ function buildVersionMdRoutes(versionId, includeDemoAuthorization) {
8086
});
8187
}
8288

89+
/** Video container shown on the markdown page VIDEO_PAGE_ROUTE_ID (no menu entry of its own). */
90+
function buildPageVideoRoute(versionId) {
91+
return buildVideoRoute(
92+
versionId,
93+
VIDEO_PAGE_ROUTE_ID,
94+
"youtube",
95+
VIDEO_IDS[PUBLIC_VIDEO_INDEX - 1],
96+
VIDEO_METAS[PUBLIC_VIDEO_INDEX].title,
97+
VIDEO_METAS[PUBLIC_VIDEO_INDEX].description,
98+
);
99+
}
100+
83101
function buildVersionVideoRoutes(versionId, includeDemoAuthorization) {
84-
return [1, 2, 3, 4].map((id) => {
102+
const menuVideoRoutes = [1, 2, 3, 4].map((id) => {
85103
const routeId = id + VIDEO_ROUTE_ID_OFFSET;
86104
const isGuarded = includeDemoAuthorization && routeId !== PUBLIC_VIDEO_ROUTE_ID;
87105
return buildVideoRoute(
@@ -94,6 +112,7 @@ function buildVersionVideoRoutes(versionId, includeDemoAuthorization) {
94112
isGuarded ? { authorization: VIDEO_ROUTE_AUTHORIZATION } : {},
95113
);
96114
});
115+
return [buildPageVideoRoute(versionId), ...menuVideoRoutes];
97116
}
98117

99118
function buildVersionAudioRoutes(versionId) {

‎cli/data/route-metas.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/** Route and video metadata for doc versions */
22

33
export const DEFAULT_HIERARCHY = { md: 0, "source-viewer": 1, html: 2, video: 3, audio: 4 };
4+
/** Page order that puts the video container above the markdown. */
5+
export const VIDEO_FIRST_HIERARCHY = { video: 0, md: 1, "source-viewer": 2, html: 3, audio: 4 };
46

57
export const ROUTE_META_ID1 = {
68
titles: { pt: "Primeiros passos", en: "Getting Started", es: "Primeros pasos" },

‎cli/tests/core/builders.test.ts‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,14 +288,27 @@ describe("buildVersionConfig", () => {
288288
const config = buildVersionConfig(versionId);
289289

290290
const video = config["routes-video"];
291-
expect(video.map((route) => route.id)).toEqual([8, 9, 10, 11]);
292-
expect(video[0].video).toEqual({
291+
expect(video.map((route) => route.id)).toEqual([5, 8, 9, 10, 11]);
292+
const videoRoute = (id: number) => {
293+
const found = video.find((route) => route.id === id);
294+
if (!found) throw new Error(`missing video route ${id}`);
295+
return found;
296+
};
297+
expect(videoRoute(8).video).toEqual({
293298
videoType: { pt: "youtube", en: "youtube", es: "youtube" },
294299
pathVideo: { pt: VIDEO_IDS[0], en: VIDEO_IDS[0], es: VIDEO_IDS[0] },
295300
});
296-
expect(video[0].authorization).toEqual({ requireExternalAuth: true, allowedProviders: EXTERNAL_PROVIDERS });
301+
expect(videoRoute(8).authorization).toEqual({ requireExternalAuth: true, allowedProviders: EXTERNAL_PROVIDERS });
297302
expect(video.filter((route) => route.authorization).map((route) => route.id)).toEqual([8, 9, 10]);
298-
expect(video[3].authorization).toBeUndefined();
303+
expect(videoRoute(11).authorization).toBeUndefined();
304+
305+
// Page 5 (Introduction to Git) shows the public video in a video container above its markdown.
306+
expect(videoRoute(5).video?.pathVideo.en).toBe(VIDEO_IDS[3]);
307+
expect(videoRoute(5).authorization).toBeUndefined();
308+
const introductionMd = config["routes-md"].find((route) => route.id === 5);
309+
const pageOrder = introductionMd?.hierarchyPage as Record<string, number> | undefined;
310+
expect(pageOrder?.video).toBeLessThan(pageOrder?.md ?? 0);
311+
expect(config["menus-header-video"].map((item) => item.id)).toEqual([8, 9, 10, 11]);
299312

300313
const [audio] = config["routes-audio"];
301314
expect(audio.id).toBe(AUDIO_META_ID12.id);

‎gitpagedocs/docs/versions/0.0.7/config.json‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,14 @@
181181
"RouteguideBrand": true,
182182
"RouteGuideSpeciFicbrand": [],
183183
"RouteguideBrandPosition": "center",
184-
"RouteguideBrandContainerTop": false
184+
"RouteguideBrandContainerTop": false,
185+
"hierarchyPage": {
186+
"video": 0,
187+
"md": 1,
188+
"source-viewer": 2,
189+
"html": 3,
190+
"audio": 4
191+
}
185192
},
186193
{
187194
"id": 6,
@@ -244,6 +251,32 @@
244251
],
245252
"routes-html": [],
246253
"routes-video": [
254+
{
255+
"id": 5,
256+
"title": {
257+
"pt": "A brief introduction to Git for beginners | GitHub",
258+
"en": "A brief introduction to Git for beginners | GitHub",
259+
"es": "A brief introduction to Git for beginners | GitHub"
260+
},
261+
"description": {
262+
"pt": "Introdução ao Git para iniciantes.",
263+
"en": "A brief introduction to Git for beginners.",
264+
"es": "Una breve introducción a Git para principiantes."
265+
},
266+
"fullscreenEnabled": true,
267+
"video": {
268+
"videoType": {
269+
"pt": "youtube",
270+
"en": "youtube",
271+
"es": "youtube"
272+
},
273+
"pathVideo": {
274+
"pt": "r8jQ9hVA2qs",
275+
"en": "r8jQ9hVA2qs",
276+
"es": "r8jQ9hVA2qs"
277+
}
278+
}
279+
},
247280
{
248281
"id": 8,
249282
"title": {

‎tools/gitpagedocs/docs/versions/0.0.7/config.json‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,14 @@
181181
"RouteguideBrand": true,
182182
"RouteGuideSpeciFicbrand": [],
183183
"RouteguideBrandPosition": "center",
184-
"RouteguideBrandContainerTop": false
184+
"RouteguideBrandContainerTop": false,
185+
"hierarchyPage": {
186+
"video": 0,
187+
"md": 1,
188+
"source-viewer": 2,
189+
"html": 3,
190+
"audio": 4
191+
}
185192
},
186193
{
187194
"id": 6,
@@ -244,6 +251,32 @@
244251
],
245252
"routes-html": [],
246253
"routes-video": [
254+
{
255+
"id": 5,
256+
"title": {
257+
"pt": "A brief introduction to Git for beginners | GitHub",
258+
"en": "A brief introduction to Git for beginners | GitHub",
259+
"es": "A brief introduction to Git for beginners | GitHub"
260+
},
261+
"description": {
262+
"pt": "Introdução ao Git para iniciantes.",
263+
"en": "A brief introduction to Git for beginners.",
264+
"es": "Una breve introducción a Git para principiantes."
265+
},
266+
"fullscreenEnabled": true,
267+
"video": {
268+
"videoType": {
269+
"pt": "youtube",
270+
"en": "youtube",
271+
"es": "youtube"
272+
},
273+
"pathVideo": {
274+
"pt": "r8jQ9hVA2qs",
275+
"en": "r8jQ9hVA2qs",
276+
"es": "r8jQ9hVA2qs"
277+
}
278+
}
279+
},
247280
{
248281
"id": 8,
249282
"title": {

‎tools/smoke/baseline.snapshot.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"generatedAt": "2026-09-28T20:57:42.003Z",
2+
"generatedAt": "2026-09-28T21:13:39.022Z",
33
"targets": [
44
"gitpagedocs/config.json",
55
"gitpagedocs/langs/en.json",
@@ -12,6 +12,6 @@
1212
"gitpagedocs/langs/en.json": "5efaddcd0429f5f2e632850a13d923d2da2a8720292bb00d642f81357772eee1",
1313
"gitpagedocs/langs/pt.json": "f29cfa74721179058914edffc078beb9a5bd30a6f811aa9ef11ff9eb89212f85",
1414
"gitpagedocs/langs/es.json": "bfd6093ba9112be0568817b40ef2d1bc73f0c96f3c8cdb3461af421df946a0cd",
15-
"gitpagedocs/docs/versions/0.0.7/config.json": "3b2f776c2f496cc73b033dbde359c4bed06fec530b7f1baadb020bf18a4f8abb"
15+
"gitpagedocs/docs/versions/0.0.7/config.json": "0b9475a60b36e3a73b7c79d0043c1032941cb0fa306e071511db9c4cc3180b21"
1616
}
1717
}

‎tools/smoke/docs-selftest.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,19 @@ function asArray(value: unknown): Array<Record<string, unknown>> {
2929
return Array.isArray(value) ? value as Array<Record<string, unknown>> : [];
3030
}
3131

32+
/**
33+
* Route ids across every route type. An id is unique within its type; routes of
34+
* different types may share one, and the viewer composes them into one page.
35+
*/
3236
function routeIds(config: Record<string, unknown>, routeKeys: string[]): Set<number> {
3337
const ids = new Set<number>();
3438
for (const key of routeKeys) {
39+
const idsOfType = new Set<number>();
3540
for (const route of asArray(config[key])) {
3641
const id = Number(route.id);
3742
check(`generated config route id is valid for ${key}`, Number.isFinite(id), String(route.id));
38-
check(`generated config route id ${id} is unique`, !ids.has(id), key);
43+
check(`generated config route id ${id} is unique`, !idsOfType.has(id), key);
44+
idsOfType.add(id);
3945
ids.add(id);
4046
}
4147
}

0 commit comments

Comments
 (0)