From c23be8a170b9b7eaa589f75a36d158ef5f3e0ce8 Mon Sep 17 00:00:00 2001 From: kirillk Date: Fri, 29 May 2026 15:00:48 -0400 Subject: [PATCH] docs(kilo-docs): address tab TOC review --- .../kilo-docs/components/TableOfContents.tsx | 16 ++++++++-------- packages/kilo-docs/components/Tabs.tsx | 18 ++++++++++++++---- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/kilo-docs/components/TableOfContents.tsx b/packages/kilo-docs/components/TableOfContents.tsx index 735383abf8..f0e9157edd 100644 --- a/packages/kilo-docs/components/TableOfContents.tsx +++ b/packages/kilo-docs/components/TableOfContents.tsx @@ -3,17 +3,18 @@ import Link from "next/link" const TAB_SYNC_EVENT = "kilo-tab-select" +function slugify(label: string) { + return label + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, "") +} + export function TableOfContents({ toc }) { const [tab, setTab] = useState("") const [activeHash, setActiveHash] = useState("") const items = toc.filter((item) => { if (!item.id || (item.level !== 2 && item.level !== 3)) return false - if ( - tab === "jetbrains" && - ["manual-installations", "open-vsx-registry", "via-vsix", "troubleshooting"].includes(item.id) - ) { - return false - } return !item.tab || item.tab.slug === tab }) @@ -28,9 +29,8 @@ export function TableOfContents({ toc }) { const sync = (e: Event) => { const label = (e as CustomEvent).detail - const item = toc.find((entry) => entry.tab?.label === label) setActiveHash(window.location.hash) - setTab(item?.tab?.slug ?? "") + setTab(slugify(label)) } update() diff --git a/packages/kilo-docs/components/Tabs.tsx b/packages/kilo-docs/components/Tabs.tsx index 16618445f8..d826b818c2 100644 --- a/packages/kilo-docs/components/Tabs.tsx +++ b/packages/kilo-docs/components/Tabs.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, Children, isValidElement, ReactNode, ReactElement } from "react" +import React, { useState, useEffect, useRef, Children, isValidElement, ReactNode, ReactElement } from "react" const TAB_SYNC_EVENT = "kilo-tab-select" @@ -50,15 +50,24 @@ export function Tabs({ children }: TabsProps) { } const [activeIndex, setActiveIndex] = useState(0) + const scroll = useRef(false) useEffect(() => { - setActiveIndex(indexFromHash()) - const onHashChange = () => setActiveIndex(indexFromHash()) + const activate = () => { + const hash = window.location.hash.slice(1) + const index = indexFromHash() + scroll.current = Boolean(hash && contains(tabs[index]?.props.children, hash)) + setActiveIndex(index) + } + + activate() + const onHashChange = activate window.addEventListener("hashchange", onHashChange) const onSync = (e: Event) => { const label = (e as CustomEvent).detail const found = tabs.findIndex((tab) => tab.props.label === label) + scroll.current = false if (found >= 0) setActiveIndex(found) } window.addEventListener(TAB_SYNC_EVENT, onSync) @@ -71,8 +80,9 @@ export function Tabs({ children }: TabsProps) { useEffect(() => { const hash = window.location.hash.slice(1) - if (!hash) return + if (!hash || !scroll.current) return + scroll.current = false requestAnimationFrame(() => document.getElementById(hash)?.scrollIntoView()) }, [activeIndex])