From 6eeca50a1be1a10e804c6ccd43fd69e43a40a6fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:35:14 +0000 Subject: [PATCH 01/14] Initial plan From 471fd6da0f14244b8bc6946ec5af4dbec3e9f251 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:45:00 +0000 Subject: [PATCH 02/14] fix: use Astro base URL for home and nav links --- src/components/Header.astro | 14 ++++++-------- src/components/Sidebar.tsx | 21 +++++++++------------ src/components/TableOfContents.astro | 14 ++++++-------- 3 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/components/Header.astro b/src/components/Header.astro index c89abff..86bb3c3 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -1,6 +1,5 @@ --- import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import Container from './Container.astro'; import Sidebar from './Sidebar'; import { getPages } from 'src/utils/pages'; @@ -10,24 +9,23 @@ const pages = await getPages(); const { pageSlug } = Astro.params; -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = + import.meta.env.BASE_URL === '/' + ? '' + : import.meta.env.BASE_URL.replace(/\/$/, ''); ---
- {projectData.data.project.title} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 3e5b8d2..55dab17 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -7,25 +7,23 @@ import type { } from 'src/utils/pages.ts'; interface SidebarProps { - baseUrl: string | undefined; + basePath: string; pages: PageCollectionEntry[]; project: ProjectCollectionEntry; slug?: string; url: URL; } -const getHref = (page: PageCollectionEntry, baseUrl: string | undefined) => { +const getHref = (page: PageCollectionEntry, basePath: string) => { if (page.data.autogenerate.type === 'home') { - return baseUrl ? `/${baseUrl}` : '/'; + return basePath || '/'; } if (page.data.autogenerate.enabled) { - return `${baseUrl ? `/${baseUrl}` : ''}/events/${ - page.data.slug || page.id - }`; + return `${basePath}/events/${page.data.slug || page.id}`; } - return `${baseUrl ? `/${baseUrl}` : ''}/pages/${page.data.slug || page.id}`; + return `${basePath}/pages/${page.data.slug || page.id}`; }; const Sidebar: React.FC = (props) => { @@ -37,9 +35,8 @@ const Sidebar: React.FC = (props) => { ); const isIndex = useMemo( - () => - `${props.baseUrl ? `/${props.baseUrl}` : ''}/tags` === props.url.pathname, - [props.baseUrl, props.url] + () => `${props.basePath}/tags` === props.url.pathname, + [props.basePath, props.url] ); // highlight the current page @@ -82,7 +79,7 @@ const Sidebar: React.FC = (props) => {
{props.pages.map((page) => ( - +

= (props) => { ))} {props.project.data.project.tags && props.project.data.project.tags.tags.length > 0 && ( - +

Index

diff --git a/src/components/TableOfContents.astro b/src/components/TableOfContents.astro index 782e204..95d8da8 100644 --- a/src/components/TableOfContents.astro +++ b/src/components/TableOfContents.astro @@ -1,17 +1,15 @@ --- import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import { getPages } from 'src/utils/pages'; const projectData = await getEntry('project', 'project'); const projectFileData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData?.data.publish.publish_pages_app - ? projectData?.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = + import.meta.env.BASE_URL === '/' + ? '' + : import.meta.env.BASE_URL.replace(/\/$/, ''); const pages = await getPages(); @@ -49,7 +47,7 @@ pages return (

{page.data.title}

@@ -61,7 +59,7 @@ pages projectFileData!.data.project.tags.tags.length > 0 && (
Index From 67e92a5324c43e1b958e0fe530b266d48b1b8487 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:46:03 +0000 Subject: [PATCH 03/14] chore: make root base-path handling explicit in sidebar --- src/components/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 55dab17..876e7ea 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -16,7 +16,7 @@ interface SidebarProps { const getHref = (page: PageCollectionEntry, basePath: string) => { if (page.data.autogenerate.type === 'home') { - return basePath || '/'; + return basePath === '' ? '/' : basePath; } if (page.data.autogenerate.enabled) { From 40081854d2fa2b33434ee7bab7949e94cc7f43d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:47:06 +0000 Subject: [PATCH 04/14] fix: normalize base path in nav link builders --- src/components/Sidebar.tsx | 23 ++++++++++++++++------- src/components/TableOfContents.astro | 5 +++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 876e7ea..c8fcafa 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -14,20 +14,29 @@ interface SidebarProps { url: URL; } +const normalizeBasePath = (basePath: string) => + basePath === '/' ? '' : basePath.replace(/\/$/, ''); + const getHref = (page: PageCollectionEntry, basePath: string) => { + const normalizedBasePath = normalizeBasePath(basePath); + if (page.data.autogenerate.type === 'home') { - return basePath === '' ? '/' : basePath; + return normalizedBasePath || '/'; } if (page.data.autogenerate.enabled) { - return `${basePath}/events/${page.data.slug || page.id}`; + return `${normalizedBasePath}/events/${page.data.slug || page.id}`; } - return `${basePath}/pages/${page.data.slug || page.id}`; + return `${normalizedBasePath}/pages/${page.data.slug || page.id}`; }; const Sidebar: React.FC = (props) => { const [show, setShow] = useState(false); + const basePath = useMemo( + () => normalizeBasePath(props.basePath), + [props.basePath] + ); const homeUuid = useMemo( () => props.pages.find((p) => p.data.autogenerate.type === 'home')?.id, @@ -35,8 +44,8 @@ const Sidebar: React.FC = (props) => { ); const isIndex = useMemo( - () => `${props.basePath}/tags` === props.url.pathname, - [props.basePath, props.url] + () => `${basePath}/tags` === props.url.pathname, + [basePath, props.url] ); // highlight the current page @@ -79,7 +88,7 @@ const Sidebar: React.FC = (props) => {
{props.pages.map((page) => ( - +

= (props) => { ))} {props.project.data.project.tags && props.project.data.project.tags.tags.length > 0 && ( - +

Index

diff --git a/src/components/TableOfContents.astro b/src/components/TableOfContents.astro index 95d8da8..2a42169 100644 --- a/src/components/TableOfContents.astro +++ b/src/components/TableOfContents.astro @@ -10,6 +10,7 @@ const basePath = import.meta.env.BASE_URL === '/' ? '' : import.meta.env.BASE_URL.replace(/\/$/, ''); +const normalizedBasePath = basePath === '/' ? '' : basePath.replace(/\/$/, ''); const pages = await getPages(); @@ -47,7 +48,7 @@ pages return (

{page.data.title}

@@ -59,7 +60,7 @@ pages projectFileData!.data.project.tags.tags.length > 0 && (
Index From 0935a3383df780a2c3b9852c0c0b6a7f6ee7b652 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:48:06 +0000 Subject: [PATCH 05/14] refactor: share base path normalization utility --- src/components/Header.astro | 6 ++---- src/components/Sidebar.tsx | 9 ++------- src/components/TableOfContents.astro | 7 ++----- src/utils/basePath.ts | 2 ++ 4 files changed, 8 insertions(+), 16 deletions(-) create mode 100644 src/utils/basePath.ts diff --git a/src/components/Header.astro b/src/components/Header.astro index 86bb3c3..c18f5d7 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -3,16 +3,14 @@ import { getEntry } from 'astro:content'; import Container from './Container.astro'; import Sidebar from './Sidebar'; import { getPages } from 'src/utils/pages'; +import { normalizeBasePath } from 'src/utils/basePath'; const projectData = await getEntry('project', 'project'); const pages = await getPages(); const { pageSlug } = Astro.params; -const basePath = - import.meta.env.BASE_URL === '/' - ? '' - : import.meta.env.BASE_URL.replace(/\/$/, ''); +const basePath = normalizeBasePath(import.meta.env.BASE_URL); ---
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index c8fcafa..c8baa52 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -5,6 +5,7 @@ import type { PageCollectionEntry, ProjectCollectionEntry, } from 'src/utils/pages.ts'; +import { normalizeBasePath } from 'src/utils/basePath'; interface SidebarProps { basePath: string; @@ -14,9 +15,6 @@ interface SidebarProps { url: URL; } -const normalizeBasePath = (basePath: string) => - basePath === '/' ? '' : basePath.replace(/\/$/, ''); - const getHref = (page: PageCollectionEntry, basePath: string) => { const normalizedBasePath = normalizeBasePath(basePath); @@ -33,10 +31,7 @@ const getHref = (page: PageCollectionEntry, basePath: string) => { const Sidebar: React.FC = (props) => { const [show, setShow] = useState(false); - const basePath = useMemo( - () => normalizeBasePath(props.basePath), - [props.basePath] - ); + const basePath = normalizeBasePath(props.basePath); const homeUuid = useMemo( () => props.pages.find((p) => p.data.autogenerate.type === 'home')?.id, diff --git a/src/components/TableOfContents.astro b/src/components/TableOfContents.astro index 2a42169..3035436 100644 --- a/src/components/TableOfContents.astro +++ b/src/components/TableOfContents.astro @@ -1,16 +1,13 @@ --- import { getEntry } from 'astro:content'; import { getPages } from 'src/utils/pages'; +import { normalizeBasePath } from 'src/utils/basePath'; const projectData = await getEntry('project', 'project'); const projectFileData = await getEntry('project', 'project'); -const basePath = - import.meta.env.BASE_URL === '/' - ? '' - : import.meta.env.BASE_URL.replace(/\/$/, ''); -const normalizedBasePath = basePath === '/' ? '' : basePath.replace(/\/$/, ''); +const normalizedBasePath = normalizeBasePath(import.meta.env.BASE_URL); const pages = await getPages(); diff --git a/src/utils/basePath.ts b/src/utils/basePath.ts new file mode 100644 index 0000000..aff90f7 --- /dev/null +++ b/src/utils/basePath.ts @@ -0,0 +1,2 @@ +export const normalizeBasePath = (basePath: string) => + basePath === '/' ? '' : basePath.replace(/\/$/, ''); From 5344b7ca9c5ad1a54153208db1b8e5a201812c19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:48:54 +0000 Subject: [PATCH 06/14] chore: simplify sidebar href normalization flow --- src/components/Sidebar.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index c8baa52..c06c04d 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -15,11 +15,9 @@ interface SidebarProps { url: URL; } -const getHref = (page: PageCollectionEntry, basePath: string) => { - const normalizedBasePath = normalizeBasePath(basePath); - +const getHref = (page: PageCollectionEntry, normalizedBasePath: string) => { if (page.data.autogenerate.type === 'home') { - return normalizedBasePath || '/'; + return normalizedBasePath === '' ? '/' : normalizedBasePath; } if (page.data.autogenerate.enabled) { From b7690f162f26ac5f3a95aaa4ecb09c85cdb4df8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:49:51 +0000 Subject: [PATCH 07/14] fix: standardize trailing slash on home links --- src/components/Header.astro | 3 ++- src/components/Sidebar.tsx | 2 +- src/components/TableOfContents.astro | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/Header.astro b/src/components/Header.astro index c18f5d7..4a2470d 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -11,6 +11,7 @@ const pages = await getPages(); const { pageSlug } = Astro.params; const basePath = normalizeBasePath(import.meta.env.BASE_URL); +const homePath = basePath === '' ? '/' : `${basePath}/`; ---
@@ -23,7 +24,7 @@ const basePath = normalizeBasePath(import.meta.env.BASE_URL); url={Astro.url} project={projectData} /> - {projectData.data.project.title} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index c06c04d..e0a1cdd 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -17,7 +17,7 @@ interface SidebarProps { const getHref = (page: PageCollectionEntry, normalizedBasePath: string) => { if (page.data.autogenerate.type === 'home') { - return normalizedBasePath === '' ? '/' : normalizedBasePath; + return normalizedBasePath === '' ? '/' : `${normalizedBasePath}/`; } if (page.data.autogenerate.enabled) { diff --git a/src/components/TableOfContents.astro b/src/components/TableOfContents.astro index 3035436..7e6cd7c 100644 --- a/src/components/TableOfContents.astro +++ b/src/components/TableOfContents.astro @@ -7,7 +7,7 @@ const projectData = await getEntry('project', 'project'); const projectFileData = await getEntry('project', 'project'); -const normalizedBasePath = normalizeBasePath(import.meta.env.BASE_URL); +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const pages = await getPages(); @@ -45,7 +45,7 @@ pages return (

{page.data.title}

@@ -57,7 +57,7 @@ pages projectFileData!.data.project.tags.tags.length > 0 && (
Index From 7a697c398bf8ae0964339d429a6fdd6eb7197914 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:38:04 +0000 Subject: [PATCH 08/14] fix: normalize base path to absolute root-prefixed format --- src/utils/basePath.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/utils/basePath.ts b/src/utils/basePath.ts index aff90f7..695f8e2 100644 --- a/src/utils/basePath.ts +++ b/src/utils/basePath.ts @@ -1,2 +1,10 @@ -export const normalizeBasePath = (basePath: string) => - basePath === '/' ? '' : basePath.replace(/\/$/, ''); +export const normalizeBasePath = (basePath: string) => { + const trimmedBasePath = basePath.trim(); + + if (trimmedBasePath === '' || trimmedBasePath === '/') { + return ''; + } + + const noTrailingSlash = trimmedBasePath.replace(/\/+$/, ''); + return noTrailingSlash.startsWith('/') ? noTrailingSlash : `/${noTrailingSlash}`; +}; From 31f43fa38cc1a44fb1fd451bb71449e3250af48a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:39:00 +0000 Subject: [PATCH 09/14] chore: preserve single trailing-slash normalization behavior --- src/utils/basePath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/basePath.ts b/src/utils/basePath.ts index 695f8e2..895b58a 100644 --- a/src/utils/basePath.ts +++ b/src/utils/basePath.ts @@ -5,6 +5,6 @@ export const normalizeBasePath = (basePath: string) => { return ''; } - const noTrailingSlash = trimmedBasePath.replace(/\/+$/, ''); + const noTrailingSlash = trimmedBasePath.replace(/\/$/, ''); return noTrailingSlash.startsWith('/') ? noTrailingSlash : `/${noTrailingSlash}`; }; From 44046b0813b379fcda4ad8753c60368a2ecc55a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:49:26 +0000 Subject: [PATCH 10/14] fix: use dynamic Astro base for sidebar links --- src/components/Header.astro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Header.astro b/src/components/Header.astro index 4a2470d..722b604 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -4,13 +4,14 @@ import Container from './Container.astro'; import Sidebar from './Sidebar'; import { getPages } from 'src/utils/pages'; import { normalizeBasePath } from 'src/utils/basePath'; +import { dynamicConfig } from 'dynamic-astro-config'; const projectData = await getEntry('project', 'project'); const pages = await getPages(); const { pageSlug } = Astro.params; -const basePath = normalizeBasePath(import.meta.env.BASE_URL); +const basePath = normalizeBasePath(dynamicConfig.base); const homePath = basePath === '' ? '/' : `${basePath}/`; --- From 3a5ca24061a8901fe07b41b7f3370618f07f05a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:25:08 +0000 Subject: [PATCH 11/14] fix: restore header base and normalize tag route URLs --- src/components/Header.astro | 3 +-- src/pages/tags/detail/[category]/[tag].astro | 20 ++++++++------------ src/pages/tags/detail/[category]/index.astro | 18 +++++++----------- src/pages/tags/detail/index.astro | 19 +++++++++---------- src/pages/tags/index.astro | 19 +++++++++---------- src/utils/basePath.ts | 11 +++++++++++ 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/components/Header.astro b/src/components/Header.astro index 722b604..4a2470d 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -4,14 +4,13 @@ import Container from './Container.astro'; import Sidebar from './Sidebar'; import { getPages } from 'src/utils/pages'; import { normalizeBasePath } from 'src/utils/basePath'; -import { dynamicConfig } from 'dynamic-astro-config'; const projectData = await getEntry('project', 'project'); const pages = await getPages(); const { pageSlug } = Astro.params; -const basePath = normalizeBasePath(dynamicConfig.base); +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const homePath = basePath === '' ? '/' : `${basePath}/`; --- diff --git a/src/pages/tags/detail/[category]/[tag].astro b/src/pages/tags/detail/[category]/[tag].astro index 1273d29..f9c4ff5 100644 --- a/src/pages/tags/detail/[category]/[tag].astro +++ b/src/pages/tags/detail/[category]/[tag].astro @@ -17,20 +17,16 @@ import type { CollectionEntry } from 'astro:content'; import { getCollection } from 'astro:content'; import { getEntry } from 'astro:content'; import { randomUUID } from 'crypto'; -import { dynamicConfig } from 'dynamic-astro-config'; import { ArrowLeft } from 'react-bootstrap-icons'; import Annotations from '@components/EventViewer/AnnotationUI/Annotations/index.astro'; import AnnotationNanostorePopulator from '@components/EventViewer/AnnotationUI/Annotations/AnnotationNanostorePopulator'; import type { DisplayedAnnotation } from '@ty/index'; import PlayerStateNanostorePopulator from '@components/EventViewer/PlayerStateNanostorePopulator'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); export const getStaticPaths = (async () => { const projectData = await getEntry('project', 'project'); @@ -50,19 +46,19 @@ const capitalizedTag = getTagDisplay(fromTagParam(tag)); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags`, + link: joinBasePath(basePath, 'tags'), }, { label: 'Tag Groups', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags/detail`, + link: joinBasePath(basePath, 'tags/detail'), }, { label: capitalizedCategory, - link: `${baseUrl ? `/${baseUrl}` : ''}/tags/detail/${category}`, + link: joinBasePath(basePath, `tags/detail/${category}`), }, { label: capitalizedTag, @@ -81,7 +77,7 @@ const categoryData = projectData.data.project.tags.tagGroups.find( ); if (!categoryData) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } type SetData = { @@ -117,7 +113,7 @@ for (const set of annotationSets) {
diff --git a/src/pages/tags/detail/[category]/index.astro b/src/pages/tags/detail/[category]/index.astro index a1676f1..85591bd 100644 --- a/src/pages/tags/detail/[category]/index.astro +++ b/src/pages/tags/detail/[category]/index.astro @@ -8,7 +8,6 @@ import Layout from '@layouts/Layout.astro'; import type { GetStaticPaths } from 'astro'; import { getCollection } from 'astro:content'; import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import { ArrowLeft, ArrowCounterclockwise } from 'react-bootstrap-icons'; import { tagColors as colors, @@ -20,14 +19,11 @@ import { sortTags, toTagParam, } from '@utils/tags.ts'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); export const getStaticPaths = (async () => { const projectData = await getEntry('project', 'project'); @@ -43,15 +39,15 @@ const capitalizedCategory = getTagDisplay(fromTagParam(category)); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags`, + link: joinBasePath(basePath, 'tags'), }, { label: 'Tag Groups', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags/detail`, + link: joinBasePath(basePath, 'tags/detail'), }, { label: capitalizedCategory, @@ -70,7 +66,7 @@ const categoryData = projectData.data.project.tags.tagGroups.find( ); if (!categoryData) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } const tags = projectData.data.project.tags.tags @@ -95,7 +91,7 @@ tags.forEach((t, idx) => (tagColors[t] = colors[idx % colors.length]));
diff --git a/src/pages/tags/detail/index.astro b/src/pages/tags/detail/index.astro index be09ea9..c75fa71 100644 --- a/src/pages/tags/detail/index.astro +++ b/src/pages/tags/detail/index.astro @@ -1,7 +1,6 @@ --- import Layout from '@layouts/Layout.astro'; import Container from '@components/Container.astro'; -import { dynamicConfig } from '../../../../dynamic-astro-config'; import Breadcrumbs from '@components/Breadcrumbs.astro'; import { getEntry } from 'astro:content'; import { getCollection } from 'astro:content'; @@ -15,23 +14,20 @@ import { setHasCategory, setsHasCategory, } from '@utils/tags'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags`, + link: joinBasePath(basePath, 'tags'), }, { label: 'Tag Groups', @@ -44,7 +40,7 @@ annotationSets.sort((a, b) => compareAnnotations(a, b, events)); // check whether there actually are any annotation files if (annotationSets.length === 0) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } const formattedCategoryNames = projectData.data.project.tags.tagGroups.map( (cat) => getTagDisplay(cat.category) @@ -104,7 +100,10 @@ const sortedTagGroups = projectData.data.project.tags.tagGroups.sort((a, b) => View Details diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro index 9bd2001..d7c2661 100644 --- a/src/pages/tags/index.astro +++ b/src/pages/tags/index.astro @@ -1,24 +1,20 @@ --- import Layout from '../../layouts/Layout.astro'; import Container from '../../components/Container.astro'; -import { dynamicConfig } from '../../../dynamic-astro-config'; import Breadcrumbs from '../../components/Breadcrumbs.astro'; import TagPill from '@components/TagUtils/TagPill'; import { getEntry } from 'astro:content'; import { getCollection } from 'astro:content'; import { toTagParam, sortTags } from '@utils/tags'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', @@ -30,7 +26,7 @@ const tags = projectData.data.project.tags; //check whether there actually are any annotation files const annotationSets = await getCollection('annotations'); if (!Object.keys(annotationSets).length) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } const detectTag = ( @@ -98,7 +94,7 @@ tags.tags.sort(sortTags).forEach((tag: { tag: string; category: string }) => {

Index

View Details @@ -117,7 +113,10 @@ tags.tags.sort(sortTags).forEach((tag: { tag: string; category: string }) => { tag.count ? ( { const noTrailingSlash = trimmedBasePath.replace(/\/$/, ''); return noTrailingSlash.startsWith('/') ? noTrailingSlash : `/${noTrailingSlash}`; }; + +export const joinBasePath = (basePath: string, segment = '') => { + const normalizedBasePath = normalizeBasePath(basePath); + const normalizedSegment = segment.trim().replace(/^\/+|\/+$/g, ''); + + if (normalizedSegment === '') { + return normalizedBasePath === '' ? '/' : `${normalizedBasePath}/`; + } + + return `${normalizedBasePath}/${normalizedSegment}`; +}; From 502321c46972a18e52bf0140fbf2465103a8b5c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:25:57 +0000 Subject: [PATCH 12/14] refactor: normalize joinBasePath home-path behavior --- src/utils/basePath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/basePath.ts b/src/utils/basePath.ts index 2d8dad3..f8d7e44 100644 --- a/src/utils/basePath.ts +++ b/src/utils/basePath.ts @@ -14,7 +14,7 @@ export const joinBasePath = (basePath: string, segment = '') => { const normalizedSegment = segment.trim().replace(/^\/+|\/+$/g, ''); if (normalizedSegment === '') { - return normalizedBasePath === '' ? '/' : `${normalizedBasePath}/`; + return normalizedBasePath === '' ? '/' : normalizedBasePath; } return `${normalizedBasePath}/${normalizedSegment}`; From 465ddc9c8a6d74e951bc3b57f05443f9809a4fea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:47:22 +0000 Subject: [PATCH 13/14] feat: use include_slug_in_base for URL generation --- dynamic-astro-config.js | 15 +++++++++++++-- src/content/config.ts | 1 + src/layouts/Layout.astro | 7 +------ src/pages/events/[pageSlug].astro | 14 +++----------- src/pages/pages/[pageSlug].astro | 12 ++---------- src/utils/events.ts | 11 ++--------- 6 files changed, 22 insertions(+), 38 deletions(-) diff --git a/dynamic-astro-config.js b/dynamic-astro-config.js index 9da77fa..68a5c81 100644 --- a/dynamic-astro-config.js +++ b/dynamic-astro-config.js @@ -8,10 +8,21 @@ import relativeLinks from 'astro-relative-links'; import project from './src/content/project/project.json'; +// include_slug_in_base is written by the admin client and is the authoritative +// source; fall back to publish_pages_app for projects generated before this +// field was introduced. +const shouldIncludeSlugInBase = project.publish.include_slug_in_base !== undefined + ? project.publish.include_slug_in_base + : project.publish.publish_pages_app; + export const dynamicConfig = { integrations: [react(), tailwind(), relativeLinks()], - site: import.meta.env.PROD ? `https://${project.project.github_org}.github.io/${project.project.slug}` : undefined, - base: import.meta.env.PROD ? project.publish.publish_pages_app ? `${project.project.slug}` : '' : 'dist', + site: import.meta.env.PROD + ? shouldIncludeSlugInBase + ? `https://${project.project.github_org}.github.io/${project.project.slug}` + : `https://${project.project.github_org}.github.io` + : undefined, + base: import.meta.env.PROD ? (shouldIncludeSlugInBase ? `${project.project.slug}` : '') : 'dist', srcDir: import.meta.env.PROD ? project.project.media_player === 'avannotate' ? './src' : project.project.media_player === 'aviary' ? diff --git a/src/content/config.ts b/src/content/config.ts index 85a3f3b..5e3b197 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -129,6 +129,7 @@ const projectCollection = defineCollection({ }), publish: z.object({ publish_pages_app: z.boolean(), + include_slug_in_base: z.boolean().nullish(), publish_sha: z.string(), publish_iso_date: z.string(), }), diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 58a1a87..caf6410 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -5,7 +5,6 @@ import '@fontsource-variable/inter'; import Header from '@components/Header.astro'; import Footer from '@components/Footer.astro'; import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import { getCollection } from 'astro:content'; interface Props { @@ -17,10 +16,6 @@ interface Props { const projectData = await getEntry('project', 'project'); const eventData = await getCollection('events'); -const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; - const { background, class: className, @@ -41,7 +36,7 @@ const manifests = eventData.map((e) => { - + diff --git a/src/pages/events/[pageSlug].astro b/src/pages/events/[pageSlug].astro index d2b17f8..a62309e 100644 --- a/src/pages/events/[pageSlug].astro +++ b/src/pages/events/[pageSlug].astro @@ -1,18 +1,10 @@ --- import type { GetStaticPaths } from 'astro'; -import { dynamicConfig } from 'dynamic-astro-config'; import Layout from '@layouts/Layout.astro'; -import { getEntry } from 'astro:content'; import EventViewer from '@components/EventViewer/index.astro'; import { getPage, getPages } from 'src/utils/pages'; import { randomUUID } from 'crypto'; - -const projectData = await getEntry('project', 'project'); - -// @ts-ignore -const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; +import { joinBasePath } from '@utils/basePath'; export const getStaticPaths = (async () => { // filter out custom pages @@ -26,13 +18,13 @@ export const getStaticPaths = (async () => { const { pageSlug } = Astro.params; if (!pageSlug) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(import.meta.env.BASE_URL)); } const page = await getPage(pageSlug); if (!page.data.autogenerate.type_id) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(import.meta.env.BASE_URL)); } const playerId = randomUUID(); diff --git a/src/pages/pages/[pageSlug].astro b/src/pages/pages/[pageSlug].astro index e63609d..5554b2c 100644 --- a/src/pages/pages/[pageSlug].astro +++ b/src/pages/pages/[pageSlug].astro @@ -1,18 +1,10 @@ --- import type { GetStaticPaths } from 'astro'; -import { dynamicConfig } from 'dynamic-astro-config'; import Layout from '@layouts/Layout.astro'; import Container from '@components/Container.astro'; -import { getEntry } from 'astro:content'; import RichText from '../../components/RichText/index.astro'; import { getPage, getPages } from 'src/utils/pages'; - -const projectData = await getEntry('project', 'project'); - -// @ts-ignore -const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; +import { joinBasePath } from '@utils/basePath'; export const getStaticPaths = (async () => { const eventPages = await getPages((p) => !p.data.autogenerate.enabled); @@ -25,7 +17,7 @@ export const getStaticPaths = (async () => { const { pageSlug } = Astro.params; if (!pageSlug) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(import.meta.env.BASE_URL)); } const page = await getPage(pageSlug); diff --git a/src/utils/events.ts b/src/utils/events.ts index 0def31b..935526b 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -1,17 +1,10 @@ -import { getEntry, type CollectionEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config.js'; +import { type CollectionEntry } from 'astro:content'; export const getCaptionSets = async ( event: CollectionEntry<'events'>, file: string, annotationSets: CollectionEntry<'annotations'>[] ) => { - const projectData = await getEntry('project', 'project'); - - const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; - let captionSets: { url: string; label: string }[]; const eventCaptionSet = event.data.audiovisual_files[file]?.caption_set; @@ -20,7 +13,7 @@ export const getCaptionSets = async ( captionSets = eventCaptionSet.map((s: any) => { const set = annotationSets.find((set) => set.id === s.annotation_page_id); return { - url: `/${baseUrl}/${s.annotation_page_id}.vtt`, + url: `${import.meta.env.BASE_URL}${s.annotation_page_id}.vtt`, label: set?.data.set || '', }; }); From a74b55339822677cb3ba173edcbd3605375fa119 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:40:53 +0000 Subject: [PATCH 14/14] Add build-time config logging to astro.config.mjs --- astro.config.mjs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/astro.config.mjs b/astro.config.mjs index 2e673d1..9a1e1fe 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,5 +1,6 @@ import { defineConfig } from 'astro/config'; import { dynamicConfig } from './dynamic-astro-config.js'; +import { createRequire } from 'module'; // We are utilizing a dynamic astro config in this // template. When the template is used to create @@ -7,5 +8,22 @@ import { dynamicConfig } from './dynamic-astro-config.js'; // the dynamic configuration will be updated to reflect // the new project +// --- Build-time config logging --- +const require = createRequire(import.meta.url); +const project = require('./src/content/project/project.json'); + +console.log('[AVAnnotate build] project.json raw values:'); +console.log(' project.slug :', project.project.slug); +console.log(' project.github_org :', project.project.github_org); +console.log(' project.media_player :', project.project.media_player); +console.log(' publish.include_slug_in_base:', project.publish.include_slug_in_base); +console.log(' publish.publish_pages_app :', project.publish.publish_pages_app); + +console.log('[AVAnnotate build] resolved Astro config values:'); +console.log(' site :', dynamicConfig.site); +console.log(' base :', dynamicConfig.base); +console.log(' srcDir:', dynamicConfig.srcDir); +// --------------------------------- + // https://astro.build/config export default defineConfig(dynamicConfig); // Do not change!