From aa1ca787d8390f7ad5f44a31171d5f5abb0f97ed Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 15:48:09 +0000 Subject: [PATCH 01/21] spike: move the docs site to Solid 2 (SolidBase #174, vite-plugin start mode) Replace SolidStart + Nitro with @solidjs/vite-plugin start mode and SolidBase's own prerender; port the osmium theme to Solid 2 APIs (Errored, Loading, onSettled, omit/merge, class arrays, For keyed={false}, clientOnly from @solidjs/web), swap solid-heroicons and solid-list for ~icons/heroicons and a local list helper. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0117BoWPLXmEUSKddsQSHkzN --- netlify.toml | 2 +- osmium/package.json | 22 ++-- osmium/src/Layout.tsx | 6 +- osmium/src/mdx-components.tsx | 69 ++++--------- osmium/src/ui/button-link.tsx | 10 +- osmium/src/ui/callout.tsx | 20 ++-- osmium/src/ui/deep-dive.tsx | 10 +- osmium/src/ui/edit-page-link.tsx | 4 +- osmium/src/ui/icon.tsx | 10 ++ osmium/src/ui/layout/language-selector.tsx | 4 +- osmium/src/ui/layout/main-header.tsx | 48 ++++----- osmium/src/ui/layout/main-navigation.tsx | 4 +- osmium/src/ui/layout/mobile-navigation.tsx | 5 +- osmium/src/ui/layout/table-of-contents.tsx | 51 ++++----- osmium/src/ui/layout/theme-selector.tsx | 13 +-- osmium/src/ui/layout/version-selector.tsx | 5 +- osmium/src/ui/not-found.tsx | 2 +- osmium/src/ui/page-issue-link.tsx | 4 +- osmium/src/ui/pagination.tsx | 6 +- osmium/src/ui/quick-links.tsx | 12 +-- osmium/src/ui/search.tsx | 115 ++++++++++++++------- osmium/tsconfig.json | 4 +- package.json | 26 ++--- pnpm-workspace.yaml | 2 - src/Document.tsx | 23 +++++ src/app.tsx | 12 ++- src/entry-client.tsx | 3 - src/entry-server.tsx | 32 ------ src/global.d.ts | 3 +- src/middleware/index.ts | 7 -- tsconfig.json | 2 +- vite.config.ts | 36 +++---- 32 files changed, 281 insertions(+), 291 deletions(-) create mode 100644 osmium/src/ui/icon.tsx create mode 100644 src/Document.tsx delete mode 100644 src/entry-client.tsx delete mode 100644 src/entry-server.tsx delete mode 100644 src/middleware/index.ts diff --git a/netlify.toml b/netlify.toml index d9df7fdcbf..d0cd25cd43 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,5 @@ [build] - publish = "dist/" + publish = "dist/client/" # "SolidStart" is one word as a brand, so this slug gets guessed/typed often. [[redirects]] diff --git a/osmium/package.json b/osmium/package.json index d4000e5dbc..12578e8ac4 100644 --- a/osmium/package.json +++ b/osmium/package.json @@ -30,20 +30,20 @@ "dependencies": { "@fontsource-variable/geist": "^5.3.0", "@fontsource-variable/geist-mono": "^5.3.0", - "@kobalte/core": "^0.13.12", - "@kobalte/solidbase": "catalog:", + "@kobalte/core": "2.0.0-alpha.3", + "@kobalte/solidbase": "file:../../spike/kobalte-solidbase-0.6.3-dev.tgz", "@kobalte/tailwindcss": "^0.9.0", "@orama/core": "^1.2.19", - "@solid-primitives/context": "^0.3.2", - "@solid-primitives/event-listener": "^2.4.6", - "@solid-primitives/marker": "^0.2.2", - "@solid-primitives/platform": "^0.2.1", - "@solid-primitives/storage": "^4.4.0", - "@solidjs/router": "1.0.0", + "@solid-primitives/context": "2.0.0-next.4", + "@solid-primitives/event-listener": "3.0.0-next.9", + "@solid-primitives/marker": "2.0.0-next.4", + "@solid-primitives/platform": "1.0.0-next.4", + "@solid-primitives/storage": "5.0.0-next.7", + "@solidjs/router": "2.0.0-next.42", "@tailwindcss/typography": "^0.5.19", - "solid-heroicons": "^3.2.4", - "solid-js": "^1.9.14", - "tailwindcss": "^4.3.3" + "solid-js": "2.0.0-rc.17", + "tailwindcss": "^4.3.3", + "@solidjs/web": "2.0.0-rc.17" }, "peerDependencies": { "tailwindcss": "^4.3.3" diff --git a/osmium/src/Layout.tsx b/osmium/src/Layout.tsx index 15046315a8..7e78c12a0c 100644 --- a/osmium/src/Layout.tsx +++ b/osmium/src/Layout.tsx @@ -1,4 +1,4 @@ -import { ErrorBoundary } from "solid-js"; +import { Errored } from "solid-js"; import { Layout } from "./ui/layout"; import { NotFound } from "./ui/not-found"; @@ -16,9 +16,9 @@ export default function (props: ParentProps) { return ( - }> + }> {props.children} - + ); diff --git a/osmium/src/mdx-components.tsx b/osmium/src/mdx-components.tsx index 94c03caced..4eb2d38af9 100644 --- a/osmium/src/mdx-components.tsx +++ b/osmium/src/mdx-components.tsx @@ -8,16 +8,16 @@ import { children, createMemo, createSignal, - splitProps, + omit, + onCleanup, } from "solid-js"; -import { isServer } from "solid-js/web"; +import { clientOnly, isServer } from "@solidjs/web"; import { cookieStorage, makePersisted, messageSync, } from "@solid-primitives/storage"; -import { clientOnly } from "@solidjs/start"; import { Callout } from "./ui/callout"; import { DeepDive } from "./ui/deep-dive"; import { Tabs, TabList, TabPanel, Tab } from "./ui/tabs"; @@ -40,7 +40,7 @@ export const DirectiveContainer = ( tabNames?: string; } & ParentProps ) => { - const _children = children(() => props.children).toArray(); + const resolved = children(() => props.children); const tabNames = createMemo(() => props.tabNames?.split("\0") ?? []); return ( @@ -49,23 +49,23 @@ export const DirectiveContainer = ( } > - {_children} + {resolved()} @@ -101,11 +101,14 @@ const TabGroup = (props: { // Groups sharing a sync key select together across the page and tabs, // and the choice persists between visits. + // The channel is client-only. + const channel = isServer ? undefined : new BroadcastChannel("tab-group"); + onCleanup(() => channel?.close()); const [openTab, setOpenTab] = makePersisted>( createSignal(props.tabNames[0]), { name: `tab-group:${props.syncKey}`, - sync: messageSync(new BroadcastChannel("tab-group")), + sync: channel && messageSync(channel), storage: cookieStorage.withOptions({ expires: new Date(Date.now() + 3e10), }), @@ -120,7 +123,7 @@ export const strong = (props: ParentProps) => ( export const EraserLink = ( props: ParentProps<{ href: string; preview: string }> ) => { - const [, rest] = splitProps(props, ["children"]); + const rest = omit(props, "children"); if (!isServer) { return ; @@ -200,44 +203,14 @@ export const h6 = (props: ParentProps) => ( {props.children} ); -export const a = (props: ParentProps & { href: string }) => { - const [, rest] = splitProps(props, ["children"]); - const resolved = children(() => props.children); - const resolvedArray = resolved.toArray(); - - // Check if the link is a code block - if ( - // Server side - (isServer && - resolvedArray[0] && - typeof resolvedArray[0] === "object" && - "t" in resolvedArray[0] && - typeof resolvedArray[0].t === "string" && - resolvedArray[0].t.substring(0, 5) === " - {resolved()} - - ); - } else { - return ( - - {resolved()} - - ); - } -}; +// A link wrapping inline code is outlined around the code; any other link gets +// the underline style. `:has()` decides, so SSR and hydration agree. +export const a = (props: ParentProps & { href: string }) => ( + +); export const p = (props: ParentProps) => (

{props.children} diff --git a/osmium/src/ui/button-link.tsx b/osmium/src/ui/button-link.tsx index fb123291ad..8a2c490d20 100644 --- a/osmium/src/ui/button-link.tsx +++ b/osmium/src/ui/button-link.tsx @@ -1,19 +1,19 @@ -import { ComponentProps, splitProps } from "solid-js"; +import { ComponentProps, omit } from "solid-js"; type ButtonLinkProps = ComponentProps<"a"> & { variant: "primary" | "secondary"; }; export const ButtonLink = (props: ButtonLinkProps) => { - const [localProps, otherProps] = splitProps(props, ["variant"]); + const otherProps = omit(props, "variant"); return ( diff --git a/osmium/src/ui/callout.tsx b/osmium/src/ui/callout.tsx index a1ce18cf7e..829eff2e5b 100644 --- a/osmium/src/ui/callout.tsx +++ b/osmium/src/ui/callout.tsx @@ -1,13 +1,11 @@ -import { Icon } from "solid-heroicons"; -import { mergeProps, type JSX, Show, untrack } from "solid-js"; -import { - lightBulb, - exclamationTriangle, - xCircle, - puzzlePiece, - bookOpen, - noSymbol, -} from "solid-heroicons/solid"; +import { Icon } from "./icon"; +import { merge, type JSX, Show, untrack } from "solid-js"; +import lightBulb from "~icons/heroicons/light-bulb-solid"; +import exclamationTriangle from "~icons/heroicons/exclamation-triangle-solid"; +import xCircle from "~icons/heroicons/x-circle-solid"; +import puzzlePiece from "~icons/heroicons/puzzle-piece-solid"; +import bookOpen from "~icons/heroicons/book-open-solid"; +import noSymbol from "~icons/heroicons/no-symbol-solid"; const styles = { note: { @@ -98,7 +96,7 @@ export type CalloutProps = { }; export function Callout(props: CalloutProps) { - const mergedProps = mergeProps({ type: "note" as CalloutType }, props); + const mergedProps = merge({ type: "note" as CalloutType }, props); const iconType = untrack(() => mergedProps.type); diff --git a/osmium/src/ui/deep-dive.tsx b/osmium/src/ui/deep-dive.tsx index 9c4d265327..98838660c7 100644 --- a/osmium/src/ui/deep-dive.tsx +++ b/osmium/src/ui/deep-dive.tsx @@ -1,9 +1,7 @@ -import { Icon } from "solid-heroicons"; -import { - chevronRight, - lightBulb, - magnifyingGlassCircle, -} from "solid-heroicons/solid"; +import { Icon } from "./icon"; +import chevronRight from "~icons/heroicons/chevron-right-solid"; +import lightBulb from "~icons/heroicons/light-bulb-solid"; +import magnifyingGlassCircle from "~icons/heroicons/magnifying-glass-circle-solid"; import type { JSX } from "solid-js"; export type DeepDiveProps = { diff --git a/osmium/src/ui/edit-page-link.tsx b/osmium/src/ui/edit-page-link.tsx index a5c64c28bb..3193ca9ebc 100644 --- a/osmium/src/ui/edit-page-link.tsx +++ b/osmium/src/ui/edit-page-link.tsx @@ -1,6 +1,6 @@ import { Component, Show } from "solid-js"; -import { Icon } from "solid-heroicons"; -import { pencilSquare } from "solid-heroicons/outline"; +import { Icon } from "./icon"; +import pencilSquare from "~icons/heroicons/pencil-square"; import { useCurrentPageData } from "@kobalte/solidbase/client"; export const EditPageLink: Component = () => { diff --git a/osmium/src/ui/icon.tsx b/osmium/src/ui/icon.tsx new file mode 100644 index 0000000000..dc24f314e2 --- /dev/null +++ b/osmium/src/ui/icon.tsx @@ -0,0 +1,10 @@ +import { type Component, type ComponentProps, omit } from "solid-js"; +import { dynamic } from "@solidjs/web"; + +export type IconComponent = Component>; + +/** Renders a heroicon imported from `~icons/heroicons/*`. */ +export function Icon(props: ComponentProps<"svg"> & { path: IconComponent }) { + const Svg = dynamic(() => props.path); + return ; +} diff --git a/osmium/src/ui/layout/language-selector.tsx b/osmium/src/ui/layout/language-selector.tsx index 845d04f292..a2dfd5c8dc 100644 --- a/osmium/src/ui/layout/language-selector.tsx +++ b/osmium/src/ui/layout/language-selector.tsx @@ -1,6 +1,6 @@ import { Component, Show } from "solid-js"; -import { Icon } from "solid-heroicons"; -import { language } from "solid-heroicons/solid"; +import { Icon } from "../icon"; +import language from "~icons/heroicons/language-solid"; import { ResolvedLocale, useLocale } from "@kobalte/solidbase/client"; import { Select } from "@kobalte/core/select"; import { OsmiumThemeConfig } from "../.."; diff --git a/osmium/src/ui/layout/main-header.tsx b/osmium/src/ui/layout/main-header.tsx index 2bf779dfb3..c74a52d217 100644 --- a/osmium/src/ui/layout/main-header.tsx +++ b/osmium/src/ui/layout/main-header.tsx @@ -3,17 +3,15 @@ import { For, Show, createSignal, - onCleanup, - onMount, + onSettled, } from "solid-js"; -import { isServer } from "solid-js/web"; +import { clientOnly } from "@solidjs/web"; import { ProjectLogo, GitHubIcon, DiscordIcon } from "../logo"; import { ThemeSelector } from "./theme-selector"; import { MobileNavigation } from "./mobile-navigation"; import { LanguageSelector } from "./language-selector"; -import { clientOnly } from "@solidjs/start"; import { useProject, useRouteConfig } from "../../utils"; import { useOsmiumThemeState } from "../../context"; @@ -28,11 +26,13 @@ interface NavLinkProps extends ComponentProps<"a"> { function NavLink(props: NavLinkProps) { return ( {props.children} @@ -51,29 +51,25 @@ export function MainHeader(_props: MainHeaderProps) { const { setNavOpen } = useOsmiumThemeState(); - if (!isServer) { + onSettled(() => { const onScroll = () => { setIsScrolled(window.scrollY > 0); }; - - onMount(() => { - onScroll(); - window.addEventListener("scroll", onScroll, { passive: true }); - }); - - onCleanup(() => { - window.removeEventListener("scroll", onScroll); - }); - } + onScroll(); + window.addEventListener("scroll", onScroll, { passive: true }); + return () => window.removeEventListener("scroll", onScroll); + }); return (

diff --git a/osmium/src/ui/layout/main-navigation.tsx b/osmium/src/ui/layout/main-navigation.tsx index e83540b04e..3a4223fa77 100644 --- a/osmium/src/ui/layout/main-navigation.tsx +++ b/osmium/src/ui/layout/main-navigation.tsx @@ -1,7 +1,7 @@ import { createMemo, createSignal, For, Show } from "solid-js"; import { useBeforeLeave, useLocation } from "@solidjs/router"; -import { Icon } from "solid-heroicons"; -import { chevronDown } from "solid-heroicons/solid"; +import { Icon } from "../icon"; +import chevronDown from "~icons/heroicons/chevron-down-solid"; import { setIsOpen } from "./mobile-navigation"; import { SidebarItem, diff --git a/osmium/src/ui/layout/mobile-navigation.tsx b/osmium/src/ui/layout/mobile-navigation.tsx index fc10c5e331..677e6951d5 100644 --- a/osmium/src/ui/layout/mobile-navigation.tsx +++ b/osmium/src/ui/layout/mobile-navigation.tsx @@ -1,6 +1,7 @@ import { Dialog } from "@kobalte/core"; -import { Icon } from "solid-heroicons"; -import { bars_3, xMark } from "solid-heroicons/solid"; +import { Icon } from "../icon"; +import bars_3 from "~icons/heroicons/bars-3-solid"; +import xMark from "~icons/heroicons/x-mark-solid"; import { MainNavigation } from "./main-navigation"; import { createSignal } from "solid-js"; diff --git a/osmium/src/ui/layout/table-of-contents.tsx b/osmium/src/ui/layout/table-of-contents.tsx index 60783a96c9..03a06efba0 100644 --- a/osmium/src/ui/layout/table-of-contents.tsx +++ b/osmium/src/ui/layout/table-of-contents.tsx @@ -1,17 +1,10 @@ -import { - Index, - type JSX, - Show, - createEffect, - createSignal, - on, -} from "solid-js"; +import { For, type JSX, Show, createEffect, createSignal } from "solid-js"; import { useCurrentPageData, TableOfContentsItemData, } from "@kobalte/solidbase/client"; import { createEventListener } from "@solid-primitives/event-listener"; -import { isServer } from "solid-js/web"; +import { isServer } from "@solidjs/web"; export const TableOfContents = () => { const data = useCurrentPageData(); @@ -23,10 +16,9 @@ export const TableOfContents = () => { Array<{ href: string; el?: HTMLElement }> >([]); - createEffect( - on(toc, (toc) => { - if (!toc) return []; - setHeadingElements( + createEffect(toc, (toc) => { + if (!toc) return; + setHeadingElements( toc .map(flattenData) .flat() @@ -35,9 +27,8 @@ export const TableOfContents = () => { return { href, el }; }) - ); - }) - ); + ); + }); if (!isServer) createEventListener(window, "scroll", () => { @@ -67,7 +58,7 @@ export const TableOfContents = () => { Overview - + {(section) => (
  • {
      - + {(subSection) => (
    1. {
    2. )} -
      +
  • )} -
    + ); @@ -111,15 +102,17 @@ const TocLink = (props: {
    {props.children} diff --git a/osmium/src/ui/layout/theme-selector.tsx b/osmium/src/ui/layout/theme-selector.tsx index ecbc0ccedc..20a6e2e04a 100644 --- a/osmium/src/ui/layout/theme-selector.tsx +++ b/osmium/src/ui/layout/theme-selector.tsx @@ -2,23 +2,24 @@ import { Component, ComponentProps, createSignal, - JSX, - onMount, + onSettled, Show, } from "solid-js"; -import { computerDesktop, moon, sun } from "solid-heroicons/solid"; +import computerDesktop from "~icons/heroicons/computer-desktop-solid"; +import moon from "~icons/heroicons/moon-solid"; +import sun from "~icons/heroicons/sun-solid"; import { Select } from "@kobalte/core/select"; import { getThemeVariant, setTheme, ThemeType, } from "@kobalte/solidbase/client"; -import { Icon } from "solid-heroicons"; +import { Icon, type IconComponent } from "../icon"; interface ThemeOption { value: ThemeType | "system"; label: string; - icon: { path: JSX.Element; outline: boolean; mini: boolean }; + icon: IconComponent; } const THEME_OPTIONS: ThemeOption[] = [ @@ -100,7 +101,7 @@ export const ThemeSelector: Component = () => { function RefreshOnMount(props: ComponentProps<"div">) { // incorrect value on server with no runtime, refresh on mount to update possibly incorrect label const [refresh, setRefresh] = createSignal(false); - onMount(() => { + onSettled(() => { setRefresh(true); }); diff --git a/osmium/src/ui/layout/version-selector.tsx b/osmium/src/ui/layout/version-selector.tsx index ec5c381f63..ff612937fc 100644 --- a/osmium/src/ui/layout/version-selector.tsx +++ b/osmium/src/ui/layout/version-selector.tsx @@ -5,8 +5,9 @@ import { useSolidBaseRouteFallbackOptions, } from "@kobalte/solidbase/client"; import type { SolidBaseRouteOption } from "@kobalte/solidbase/config/route"; -import { Icon } from "solid-heroicons"; -import { chevronUpDown, tag } from "solid-heroicons/solid"; +import { Icon } from "../icon"; +import chevronUpDown from "~icons/heroicons/chevron-up-down-solid"; +import tag from "~icons/heroicons/tag-solid"; export default function VersionSelector() { const [open, setOpen] = createSignal(false); diff --git a/osmium/src/ui/not-found.tsx b/osmium/src/ui/not-found.tsx index b001b8d2f0..8a9b735a35 100644 --- a/osmium/src/ui/not-found.tsx +++ b/osmium/src/ui/not-found.tsx @@ -1,6 +1,6 @@ import { Title } from "@solidjs/meta"; import { Layout } from "./layout"; -import { HttpStatusCode } from "@solidjs/start"; +import { HttpStatusCode } from "@kobalte/solidbase/client"; import { useProject } from "../utils"; export function NotFound() { diff --git a/osmium/src/ui/page-issue-link.tsx b/osmium/src/ui/page-issue-link.tsx index 03752ed738..e80b2c8624 100644 --- a/osmium/src/ui/page-issue-link.tsx +++ b/osmium/src/ui/page-issue-link.tsx @@ -1,6 +1,6 @@ import { Component, createMemo } from "solid-js"; -import { Icon } from "solid-heroicons"; -import { exclamationTriangle } from "solid-heroicons/outline"; +import { Icon } from "./icon"; +import exclamationTriangle from "~icons/heroicons/exclamation-triangle"; import { useLocation } from "@solidjs/router"; import { useCurrentPageData } from "@kobalte/solidbase/client"; import { useRouteConfig } from "../utils"; diff --git a/osmium/src/ui/pagination.tsx b/osmium/src/ui/pagination.tsx index 6f8ffddd8e..5e35736497 100644 --- a/osmium/src/ui/pagination.tsx +++ b/osmium/src/ui/pagination.tsx @@ -1,4 +1,4 @@ -import { Show, Suspense } from "solid-js"; +import { Loading, Show } from "solid-js"; import { usePrevNext } from "@kobalte/solidbase/client"; import { RelativePageConfig, useOsmiumThemeFrontmatter } from "../frontmatter"; @@ -22,7 +22,7 @@ export function Pagination(_props: Pagination) { typeof r === "object" ? r.link : undefined; return ( - + - + ); } diff --git a/osmium/src/ui/quick-links.tsx b/osmium/src/ui/quick-links.tsx index a5aa33bf79..17fbc59390 100644 --- a/osmium/src/ui/quick-links.tsx +++ b/osmium/src/ui/quick-links.tsx @@ -1,12 +1,10 @@ -import { Icon } from "solid-heroicons"; +import { Icon } from "./icon"; import { JSXElement, ParentComponent, Show } from "solid-js"; -import { - academicCap, - codeBracketSquare, - pencilSquare, - userGroup, -} from "solid-heroicons/solid"; +import academicCap from "~icons/heroicons/academic-cap-solid"; +import codeBracketSquare from "~icons/heroicons/code-bracket-square-solid"; +import pencilSquare from "~icons/heroicons/pencil-square-solid"; +import userGroup from "~icons/heroicons/user-group-solid"; export type QuickLinksProps = { icon: string; diff --git a/osmium/src/ui/search.tsx b/osmium/src/ui/search.tsx index 052c37ef52..ac9e53bd77 100644 --- a/osmium/src/ui/search.tsx +++ b/osmium/src/ui/search.tsx @@ -1,16 +1,15 @@ import { OramaCloud, type SearchResult } from "@orama/core"; import { + type Accessor, createEffect, createSignal, Show, For, - Suspense, - startTransition, + Loading, createMemo, } from "solid-js"; import { Dialog } from "@kobalte/core/dialog"; -import { A, createAsync, useNavigate, usePreloadRoute } from "@solidjs/router"; -import { createList } from "solid-list"; +import { useNavigate, usePreloadRoute } from "@solidjs/router"; import { createMarker, makeSearchRegex } from "@solid-primitives/marker"; import { createEventListener } from "@solid-primitives/event-listener"; import { isAppleDevice } from "@solid-primitives/platform"; @@ -51,7 +50,7 @@ export function Search() { const [searchTerm, setSearchTerm] = createSignal(""); const [resultRefs, setResultRefs] = createSignal([]); - const result = createAsync( + const result = createMemo( async () => { const _searchTerm = searchTerm(); if (!_searchTerm) return {}; @@ -85,11 +84,9 @@ export function Search() { }, {} as Record["hits"]> ); - setActive(0); - setResultRefs([]); return groupedHits; }, - { initialValue: {} } + { loadingValue: {} as Record["hits"]> } ); const resultArray = () => Object.values(result()).flatMap((hits) => hits); @@ -102,16 +99,30 @@ export function Search() { handleTab: false, }); - createEffect(() => { - const _active = active(); - if (_active === null) return; - const ref = resultRefs()[_active]; - if (ref) ref.scrollIntoView({ block: "nearest" }); - const path = resultArray()[_active]?.document.path; - if (!path) return; - preload(new URL(path, config().siteUrl), { preloadData: true }); + // A new result set starts again from its first hit. + createEffect(result, () => { + setActive(0); + setResultRefs([]); }); + createEffect( + () => { + const _active = active(); + if (_active === null) return; + return { + ref: resultRefs()[_active], + path: resultArray()[_active]?.document.path, + siteUrl: config().siteUrl, + }; + }, + (next) => { + if (!next) return; + next.ref?.scrollIntoView({ block: "nearest" }); + if (next.path) + preload(new URL(next.path, next.siteUrl), { preloadData: true }); + } + ); + createEventListener(window, "keydown", (e: KeyboardEvent) => { if ((e.ctrlKey || e.metaKey) && e.key === "k") { e.preventDefault(); @@ -191,9 +202,7 @@ export function Search() { value={searchTerm()} class="w-full rounded border border-blue-100 bg-white px-9 py-2 ring-2 ring-blue-400 focus:outline-none focus-visible:border focus-visible:border-blue-400 focus-visible:ring-2 dark:bg-slate-800" onInput={(e) => - startTransition(() => - setSearchTerm((e.target as HTMLInputElement).value) - ) + setSearchTerm((e.target as HTMLInputElement).value) } onFocus={() => setActive(0)} onBlur={() => setActive(null)} @@ -225,14 +234,14 @@ export function Search() {
    - +

    No results for "{searchTerm()}"

    Believe this query should return results?{" "} - Let us know - + .

    @@ -278,13 +287,15 @@ export function Search() { class="scroll-my-1.5" > setActive(itemIndex)} > @@ -305,7 +316,7 @@ export function Search() { )} -
    +
    - - +
    @@ -837,10 +848,10 @@ export function Search() { function KeyboardShortcut(props: { key: string; class?: string }) { return ( {props.key} @@ -857,3 +868,37 @@ function trimText(text: string, term: string, wordPadding: number) { wordsBeforeTerm.slice(startWordIndex).join(" ") + text.substring(termIndex); return (startWordIndex > 0 ? "..." : "") + trimmedResult; } + +/** Arrow/Home/End navigation over a list of item indices, looping at the ends. */ +function createList(props: { + items: Accessor; + initialActive: number | null; + handleTab?: boolean; +}) { + const [active, setActive] = createSignal(props.initialActive); + const onKeyDown = (event: KeyboardEvent) => { + const items = props.items(); + if (items.length === 0) return; + const current = active(); + const index = current === null ? -1 : items.indexOf(current); + let next: number | undefined; + switch (event.key) { + case "ArrowDown": + next = items[(index + 1) % items.length]; + break; + case "ArrowUp": + next = items[index <= 0 ? items.length - 1 : index - 1]; + break; + case "Home": + next = items[0]; + break; + case "End": + next = items[items.length - 1]; + break; + } + if (next === undefined) return; + event.preventDefault(); + setActive(next); + }; + return { active, setActive, onKeyDown }; +} diff --git a/osmium/tsconfig.json b/osmium/tsconfig.json index 11a66f5076..b568907d3d 100644 --- a/osmium/tsconfig.json +++ b/osmium/tsconfig.json @@ -11,14 +11,14 @@ "noFallthroughCasesInSwitch": false, "isolatedModules": true, "jsx": "preserve", - "jsxImportSource": "solid-js", + "jsxImportSource": "@solidjs/web", "resolveJsonModule": true, "skipLibCheck": true, "strict": true, "declaration": true, "sourceMap": true, "lib": ["dom", "esnext"], - "types": ["solid-js"], + "types": ["vite/client"], "rootDir": "./src", "outDir": "./dist" }, diff --git a/package.json b/package.json index 64554688f5..496c25a5a3 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "sync:orama": "pnpm build && node scripts/sync-orama.mjs", "dev": "vite dev", "build": "vite build && pnpm build:llms", - "build:llms": "node scripts/build-llms.mjs", - "start": "vite start", + "build:llms": "node scripts/build-llms.mjs dist/client", + "start": "vite preview", "e2e": "npx playwright test", "format": "prettier . --write", "lint:fix": "eslint --flag unstable_native_nodejs_ts_config . --fix", @@ -21,21 +21,18 @@ "lint:tone": "node scripts/lint-tone.mjs" }, "dependencies": { - "@kobalte/core": "^0.13.12", - "@kobalte/solidbase": "catalog:", - "@solidjs/meta": "^0.29.4", - "@solidjs/router": "^1.0.0", - "@solidjs/start": "2.0.0", + "@kobalte/solidbase": "file:../spike/kobalte-solidbase-0.6.3-dev.tgz", + "@solidjs/meta": "1.0.0-next.2", + "@solidjs/router": "2.0.0-next.42", "@tailwindcss/vite": "^4.3.3", "dotenv": "^17.4.2", - "nitro": "3.0.260610-beta", "resolve": "1.22.12", - "solid-heroicons": "^3.2.4", - "solid-js": "1.9.14", + "solid-js": "2.0.0-rc.17", "solid-list": "^0.3.0", "solidbase-osmium": "workspace:*", "vite": "8.2.1", - "zod": "^4.4.3" + "zod": "^4.4.3", + "@solidjs/web": "2.0.0-rc.17" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -53,10 +50,9 @@ "prettier-plugin-tailwindcss": "^0.8.1", "tailwindcss": "^4.3.3", "typescript": "npm:@typescript/typescript6@^6.0.2", - "typescript-eslint": "^8.65.0" - }, - "overrides": { - "@solidjs/start": "2.0.0" + "typescript-eslint": "^8.65.0", + "@solidjs/vite-plugin": "3.0.0-next.51", + "@iconify-json/heroicons": "^1.2.3" }, "engines": { "node": ">=24", diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 184d41c2f9..4188761e98 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -15,5 +15,3 @@ catalog: allowBuilds: esbuild: true -overrides: - "@solidjs/router": "^1.0.0" diff --git a/src/Document.tsx b/src/Document.tsx new file mode 100644 index 0000000000..2f96e999c8 --- /dev/null +++ b/src/Document.tsx @@ -0,0 +1,23 @@ +import { getHtmlProps } from "@kobalte/solidbase/server"; +import { HydrationScript } from "@solidjs/web"; +import type { ParentProps } from "solid-js"; + +// Document shell for @solidjs/vite-plugin start mode: the app arrives as +// children and the client entry script is injected into . +export default function Document(props: ParentProps) { + return ( + + + + + + +