diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index d505ce982b..75ea42d333 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -93,7 +93,7 @@ func handleFileViewRenderMarkup(ctx *context.Context, filename string, sniffedTy return true } - ctx.Data["FileTocHTML"] = renderSidebarTocHTML(rctx) + ctx.Data["FileSidebarHTML"] = renderSidebarTocHTML(rctx) return true } diff --git a/routers/web/repo/view_readme.go b/routers/web/repo/view_readme.go index 8999339bab..3cc35c35ec 100644 --- a/routers/web/repo/view_readme.go +++ b/routers/web/repo/view_readme.go @@ -207,7 +207,7 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil delete(ctx.Data, "IsMarkup") } - ctx.Data["ReadmeTocHTML"] = renderSidebarTocHTML(rctx) + ctx.Data["FileSidebarHTML"] = renderSidebarTocHTML(rctx) } if ctx.Data["IsMarkup"] != true { diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index f638e73f15..7010e9d690 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -35,11 +35,13 @@ {{end}}
- {{if or .ReadmeTocHTML .FileTocHTML}} - - {{end}} +
+ {{end}} {{/* this componment is also controlled by frontend plugin renders */}}
{{if .IsRepresentableAsText}} @@ -144,9 +146,9 @@
{{end}} - {{if or .ReadmeTocHTML .FileTocHTML}} -
- {{or .ReadmeTocHTML .FileTocHTML}} + {{if .FileSidebarHTML}} + {{end}} diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index 110f8f7389..f686171375 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -79,8 +79,8 @@ transition: margin-right 0.2s ease; } -/* When TOC is visible, reserve space on the right (only for file view, not home page) */ -.repo-view-content.toc-visible { +/* When sidebar is visible, reserve space on the right (only for file view, not home page) */ +.repo-view-content.sidebar-visible { margin-right: 270px; } @@ -112,8 +112,8 @@ text-decoration: none; } -/* TOC as a fixed sidebar panel */ -.file-view-toc { +/* File view sidebar panel (e.g., TOC for markdown files) */ +.file-view-sidebar { position: fixed; top: 120px; /* Will be adjusted by JS to align with file content */ right: 0.5rem; @@ -129,21 +129,21 @@ transition: opacity 0.15s ease; } -/* Show TOC after JS has positioned it */ -.file-view-toc.toc-positioned { +/* Show sidebar after JS has positioned it */ +.file-view-sidebar.sidebar-positioned { opacity: 1; } /* Hidden state - using custom class to avoid Tailwind conflicts */ -.file-view-toc.toc-panel-hidden { +.file-view-sidebar.sidebar-panel-hidden { display: none; } -.file-view-toc details { +.file-view-sidebar details { font-size: 13px; } -.file-view-toc summary { +.file-view-sidebar summary { font-weight: var(--font-weight-semibold); cursor: pointer; padding: 8px 0; @@ -152,17 +152,17 @@ margin-bottom: 8px; } -.file-view-toc ul { +.file-view-sidebar ul { margin: 0; list-style: none; padding: 5px 0 5px 1em; } -.file-view-toc ul ul { +.file-view-sidebar ul ul { border-left: 1px dashed var(--color-secondary); } -.file-view-toc a { +.file-view-sidebar a { display: block; padding: 6px 10px; color: var(--color-text); @@ -172,28 +172,28 @@ border-radius: var(--border-radius); } -.file-view-toc a:hover { +.file-view-sidebar a:hover { color: var(--color-primary); background: var(--color-hover); } -/* TOC toggle button active state - when TOC is visible */ -#toggle-toc-btn.active { +/* Sidebar toggle button active state - when sidebar is visible */ +#toggle-sidebar-btn.active { color: var(--color-primary); } -/* Hide TOC sidebar on small screens */ +/* Hide sidebar on small screens */ @media (max-width: 1400px) { - .file-view-toc { + .file-view-sidebar { display: none !important; } - #toggle-toc-btn { + #toggle-sidebar-btn { display: none; } - /* Don't reserve space for TOC on small screens */ - .repo-view-content.toc-visible { + /* Don't reserve space for sidebar on small screens */ + .repo-view-content.sidebar-visible { margin-right: 0; } } diff --git a/web_src/js/features/file-view.ts b/web_src/js/features/file-view.ts index 430a6d5ef8..caa4a036db 100644 --- a/web_src/js/features/file-view.ts +++ b/web_src/js/features/file-view.ts @@ -59,32 +59,32 @@ async function renderRawFileToContainer(container: HTMLElement, rawFileLink: str } } -function updateTocPosition(elFileView: HTMLElement, tocSidebar: HTMLElement): void { +function updateSidebarPosition(elFileView: HTMLElement, sidebar: HTMLElement): void { const fileHeader = elFileView.querySelector('.file-header'); if (!fileHeader) return; const headerRect = fileHeader.getBoundingClientRect(); - // Align TOC top with the file header top, with a minimum of 12px from viewport top + // Align sidebar top with the file header top, with a minimum of 12px from viewport top const topPos = Math.max(12, headerRect.top); - tocSidebar.style.top = `${topPos}px`; + sidebar.style.top = `${topPos}px`; - // Position TOC right next to the content (works for both file view and home page) + // Position sidebar right next to the content (works for both file view and home page) const segment = elFileView.querySelector('.ui.segment'); if (segment) { const segmentRect = segment.getBoundingClientRect(); const leftPos = segmentRect.right + 8; // 8px gap from content - tocSidebar.style.left = `${leftPos}px`; - tocSidebar.style.right = 'auto'; + sidebar.style.left = `${leftPos}px`; + sidebar.style.right = 'auto'; } - // Mark as positioned to show the TOC (prevents flicker) - tocSidebar.classList.add('toc-positioned'); + // Mark as positioned to show the sidebar (prevents flicker) + sidebar.classList.add('sidebar-positioned'); } -function initTocToggle(elFileView: HTMLElement): void { - const toggleBtn = elFileView.querySelector('#toggle-toc-btn'); - const tocSidebar = elFileView.querySelector('.file-view-toc'); - if (!toggleBtn || !tocSidebar) return; +function initSidebarToggle(elFileView: HTMLElement): void { + const toggleBtn = elFileView.querySelector('#toggle-sidebar-btn'); + const sidebar = elFileView.querySelector('.file-view-sidebar'); + if (!toggleBtn || !sidebar) return; // Check if we're in file view (not home page) - only file view needs margin adjustment const repoViewContent = elFileView.closest('.repo-view-content'); @@ -92,26 +92,26 @@ function initTocToggle(elFileView: HTMLElement): void { // Helper to update position const updatePosition = () => { - if (!tocSidebar.classList.contains('toc-panel-hidden')) { - updateTocPosition(elFileView, tocSidebar); + if (!sidebar.classList.contains('sidebar-panel-hidden')) { + updateSidebarPosition(elFileView, sidebar); } }; - // Helper to show TOC with proper positioning - const showToc = () => { + // Helper to show sidebar with proper positioning + const showSidebar = () => { toggleBtn.classList.add('active'); - // Wait for margin to take effect before showing and positioning TOC + // Wait for margin to take effect before showing and positioning sidebar const showAfterLayout = () => { - tocSidebar.classList.remove('toc-panel-hidden'); + sidebar.classList.remove('sidebar-panel-hidden'); requestAnimationFrame(() => { - updateTocPosition(elFileView, tocSidebar); + updateSidebarPosition(elFileView, sidebar); }); }; - // For file view, first add margin, wait for layout, then show TOC - if (isFileView) { - repoViewContent.classList.add('toc-visible'); + // For file view, first add margin, wait for layout, then show sidebar + if (isFileView && repoViewContent) { + repoViewContent.classList.add('sidebar-visible'); // Wait for CSS transition to complete (200ms) before calculating position setTimeout(showAfterLayout, 220); } else { @@ -120,28 +120,28 @@ function initTocToggle(elFileView: HTMLElement): void { } }; - // Helper to hide TOC - const hideToc = () => { - tocSidebar.classList.add('toc-panel-hidden'); - tocSidebar.classList.remove('toc-positioned'); + // Helper to hide sidebar + const hideSidebar = () => { + sidebar.classList.add('sidebar-panel-hidden'); + sidebar.classList.remove('sidebar-positioned'); toggleBtn.classList.remove('active'); - if (isFileView) { - repoViewContent.classList.remove('toc-visible'); + if (isFileView && repoViewContent) { + repoViewContent.classList.remove('sidebar-visible'); } }; // Restore saved state from localStorage (default to hidden) - const savedState = localStorage.getItem('file-view-toc-visible'); + const savedState = localStorage.getItem('file-view-sidebar-visible'); const isVisible = savedState === 'true'; // default to hidden // Apply initial state if (isVisible) { - showToc(); + showSidebar(); } else { - hideToc(); + hideSidebar(); } - // Update TOC position on resize/scroll to keep aligned with file content + // Update sidebar position on resize/scroll to keep aligned with file content const resizeObserver = new ResizeObserver(() => { updatePosition(); }); @@ -160,13 +160,13 @@ function initTocToggle(elFileView: HTMLElement): void { } toggleBtn.addEventListener('click', () => { - const isCurrentlyVisible = !tocSidebar.classList.contains('toc-panel-hidden'); + const isCurrentlyVisible = !sidebar.classList.contains('sidebar-panel-hidden'); if (isCurrentlyVisible) { - hideToc(); - localStorage.setItem('file-view-toc-visible', 'false'); + hideSidebar(); + localStorage.setItem('file-view-sidebar-visible', 'false'); } else { - showToc(); - localStorage.setItem('file-view-toc-visible', 'true'); + showSidebar(); + localStorage.setItem('file-view-sidebar-visible', 'true'); } }); } @@ -175,8 +175,8 @@ export function initRepoFileView(): void { registerGlobalInitFunc('initRepoFileView', async (elFileView: HTMLElement) => { initPluginsOnce(); - // Initialize TOC toggle functionality - initTocToggle(elFileView); + // Initialize sidebar toggle functionality (e.g., TOC for markdown files) + initSidebarToggle(elFileView); const rawFileLink = elFileView.getAttribute('data-raw-file-link')!; const mimeType = elFileView.getAttribute('data-mime-type') || ''; // not used yet