From 505ba0823748346990a886b6c12d47d0d65c4d78 Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Fri, 16 Jan 2026 17:02:38 -0800 Subject: [PATCH 1/2] fix(docs): Cancel active stream when clearing chat The clear chat button (trash icon) now calls stop() before clearing messages, preventing stream chunks from continuing to appear after the chat is cleared. Also adds onStop prop to PromptInputSubmit so the stop button works. --- .../site/components/ai-elements/prompt-input.tsx | 16 ++++++++++++++-- docs/site/components/geistdocs/chat.tsx | 6 ++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/site/components/ai-elements/prompt-input.tsx b/docs/site/components/ai-elements/prompt-input.tsx index d6fdfa9d0e239..77807bef67286 100644 --- a/docs/site/components/ai-elements/prompt-input.tsx +++ b/docs/site/components/ai-elements/prompt-input.tsx @@ -984,6 +984,7 @@ export const PromptInputActionMenuItem = ({ export type PromptInputSubmitProps = ComponentProps & { status?: ChatStatus; + onStop?: () => void; }; export const PromptInputSubmit = ({ @@ -991,6 +992,7 @@ export const PromptInputSubmit = ({ variant = "default", size = "icon-sm", status, + onStop, children, ...props }: PromptInputSubmitProps) => { @@ -1004,12 +1006,22 @@ export const PromptInputSubmit = ({ Icon = ; } + const isStreaming = status === "streaming" || status === "submitted"; + + const handleClick = (e: React.MouseEvent) => { + if (isStreaming && onStop) { + e.preventDefault(); + onStop(); + } + }; + return ( diff --git a/docs/site/components/geistdocs/chat.tsx b/docs/site/components/geistdocs/chat.tsx index af24f606a1e1f..b402bc6ed5e5c 100644 --- a/docs/site/components/geistdocs/chat.tsx +++ b/docs/site/components/geistdocs/chat.tsx @@ -129,7 +129,7 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => { const { initialMessages, isLoading, saveMessages, clearMessages } = useChatPersistence(); - const { messages, sendMessage, status, setMessages } = useChat({ + const { messages, sendMessage, status, setMessages, stop } = useChat({ transport: new DefaultChatTransport({ api: basePath ? `${basePath}/api/chat` : "/api/chat" }), @@ -188,6 +188,8 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => { const handleClearChat = async () => { try { + // Cancel any active stream first + stop(); await clearMessages(); setMessages([]); toast.success("Chat history cleared"); @@ -342,7 +344,7 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => {

{localPrompt.length} / 1000

- + From 001d8f43e23d8a59b89a65a9c9e66c56af05549b Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Fri, 16 Jan 2026 17:45:39 -0800 Subject: [PATCH 2/2] feat(docs): Add version warning for old docs subdomains --- .../site/app/[lang]/docs/[[...slug]]/page.tsx | 1 + docs/site/components/geistdocs/sidebar.tsx | 7 +- docs/site/components/version-warning.tsx | 90 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 docs/site/components/version-warning.tsx diff --git a/docs/site/app/[lang]/docs/[[...slug]]/page.tsx b/docs/site/app/[lang]/docs/[[...slug]]/page.tsx index cd60cc3ffb1d9..37feb1c8f8b2c 100644 --- a/docs/site/app/[lang]/docs/[[...slug]]/page.tsx +++ b/docs/site/app/[lang]/docs/[[...slug]]/page.tsx @@ -15,6 +15,7 @@ import { getMDXComponents } from "@/components/geistdocs/mdx-components"; import { OpenInChat } from "@/components/geistdocs/open-in-chat"; import { RemoteCacheCounter } from "@/components/remote-cache-counter"; import { ScrollTop } from "@/components/geistdocs/scroll-top"; + import { Separator } from "@/components/ui/separator"; import { getLLMText, getPageImage, source } from "@/lib/geistdocs/source"; diff --git a/docs/site/components/geistdocs/sidebar.tsx b/docs/site/components/geistdocs/sidebar.tsx index 988e1cea1eef1..6ce9a62064b55 100644 --- a/docs/site/components/geistdocs/sidebar.tsx +++ b/docs/site/components/geistdocs/sidebar.tsx @@ -25,6 +25,7 @@ import { import { github, nav } from "@/geistdocs"; import { useSidebarContext } from "@/hooks/geistdocs/use-sidebar"; import { SearchButton } from "./search"; +import { VersionWarning } from "@/components/version-warning"; export const Sidebar = () => { const { root } = useTreeContext(); @@ -55,6 +56,7 @@ export const Sidebar = () => { data-sidebar-placeholder >
+ {renderSidebarList(root.children)}
@@ -104,7 +106,10 @@ export const Sidebar = () => { ) : null} -
{renderSidebarList(root.children)}
+
+ + {renderSidebarList(root.children)} +
diff --git a/docs/site/components/version-warning.tsx b/docs/site/components/version-warning.tsx new file mode 100644 index 0000000000000..29fdb790f0da9 --- /dev/null +++ b/docs/site/components/version-warning.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { TriangleAlert } from "lucide-react"; +import Link from "next/link"; +import { useEffect, useState } from "react"; + +const PRODUCTION_DOMAIN = "turborepo.dev"; +const NPM_REGISTRY_URL = "https://registry.npmjs.org/turbo/latest"; + +/** + * Convert subdomain format to semver for comparison. + * Subdomain format: "v2-3-1" -> "2.3.1" + */ +function subdomainToSemver(subdomain: string): string { + return subdomain.replace(/^v/, "").replace(/-/g, "."); +} + +/** + * Compare two semver strings. + * Returns true if `a` is older than `b`. + */ +function isOlderVersion(a: string, b: string): boolean { + const aParts = a.split(".").map(Number); + const bParts = b.split(".").map(Number); + + for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { + const aVal = aParts[i] || 0; + const bVal = bParts[i] || 0; + if (aVal < bVal) return true; + if (aVal > bVal) return false; + } + return false; +} + +export function VersionWarning() { + const [isOldVersion, setIsOldVersion] = useState(false); + const [subdomainVersion, setSubdomainVersion] = useState(""); + + useEffect(() => { + const host = window.location.host; + + // Check if we're on a subdomain of turborepo.dev (e.g., v2-3-1.turborepo.dev) + if (host === PRODUCTION_DOMAIN || !host.endsWith(`.${PRODUCTION_DOMAIN}`)) { + return; + } + + // Extract version from subdomain (e.g., "v2-3-1" from "v2-3-1.turborepo.dev") + const subdomain = host.replace(`.${PRODUCTION_DOMAIN}`, ""); + setSubdomainVersion(subdomain); + + const currentSemver = subdomainToSemver(subdomain); + + // Fetch latest version from npm to compare + fetch(NPM_REGISTRY_URL) + .then((res) => res.json()) + .then((data) => { + const latestVersion = data.version as string; + + if (isOlderVersion(currentSemver, latestVersion)) { + setIsOldVersion(true); + } + }) + .catch(() => { + // If we can't fetch npm, assume it's old to be safe + setIsOldVersion(true); + }); + }, []); + + if (!isOldVersion) { + return null; + } + + return ( +
+
+ + Old Version ({subdomainVersion}) +
+

+ You're viewing docs for an out-of-date version of Turborepo.{" "} + + View latest docs → + +

+
+ ); +}