diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc index 7e181a008..9a7e64dfd 100644 --- a/.oxfmtrc.jsonc +++ b/.oxfmtrc.jsonc @@ -9,5 +9,5 @@ "website/**/routeTree.gen.ts", "website/src/routes/blog/-content/assets/**/*.json", ], - "experimentalSortImports": {}, + "sortImports": {}, } diff --git a/package.json b/package.json index 2e0668f95..416432ab6 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@types/node": "catalog:", "knip": "^6.26.0", "lefthook": "^2.1.10", - "oxfmt": "^0.59.0", + "oxfmt": "catalog:", "oxlint": "^1.74.0", "oxlint-tsgolint": "^0.24.0", "playwright": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 460669d9e..46d0bcb25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,9 @@ settings: catalogs: default: + '@oxfmt/binding-linux-x64-gnu': + specifier: ^0.59.0 + version: 0.59.0 '@standard-schema/spec': specifier: ^1.1.0 version: 1.1.0 @@ -36,6 +39,9 @@ catalogs: mix-n-matchers: specifier: ^2.3.0 version: 2.3.0 + oxfmt: + specifier: ^0.59.0 + version: 0.59.0 playwright: specifier: ^1.61.1 version: 1.61.1 @@ -76,7 +82,7 @@ importers: specifier: ^2.1.10 version: 2.1.10 oxfmt: - specifier: ^0.59.0 + specifier: 'catalog:' version: 0.59.0 oxlint: specifier: ^1.74.0 @@ -344,6 +350,9 @@ importers: normalize.css: specifier: ^8.0.1 version: 8.0.1 + oxfmt: + specifier: 'catalog:' + version: 0.59.0 prismjs: specifier: ^1.30.0 version: 1.30.0 @@ -528,6 +537,10 @@ importers: workbox-window: specifier: ^7.4.1 version: 7.4.1 + optionalDependencies: + '@oxfmt/binding-linux-x64-gnu': + specifier: 'catalog:' + version: 0.59.0 packages: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 816756369..89b37f61d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,6 +13,7 @@ allowBuilds: sharp: false catalog: + "@oxfmt/binding-linux-x64-gnu": ^0.59.0 "@standard-schema/spec": ^1.1.0 "@ttsc/unplugin": "^0.18.4" "@types/node": 24.0.0 @@ -24,6 +25,7 @@ catalog: "@vitest/pretty-format": ^4.1.10 dedent: ^1.7.2 "mix-n-matchers": "^2.3.0" + oxfmt: "^0.59.0" playwright: ^1.61.1 react: ^19.2.7 react-dom: ^19.2.7 diff --git a/utils/src/index.ts b/utils/src/index.ts index b593e873c..31baeb64e 100644 --- a/utils/src/index.ts +++ b/utils/src/index.ts @@ -423,3 +423,25 @@ export function* range( export function identity(value: T): T { return value; } + +/** + * Hash function that returns a 53-bit hash of a string, with an optional prefix (defaulting to the string length). + * @remarks Fast and deterministic. Not suitable for cryptographic purposes. + */ +export function getCyrb53Hash(str: string, prefix = str.length.toString()): string { + let h1 = 0xdeadbeef ^ str.length; + let h2 = 0x41c6ce57 ^ str.length; + + for (let i = 0; i < str.length; i++) { + const ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + + h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909); + + const hash53 = 4294967296 * (2097151 & h2) + (h1 >>> 0); + + return `${prefix}-${hash53.toString(36)}`; +} diff --git a/website/netlify.toml b/website/netlify.toml index 59b35b122..b1cfe8936 100644 --- a/website/netlify.toml +++ b/website/netlify.toml @@ -2,6 +2,17 @@ command = "pnpm install && cd ./website && pnpm build" publish = "website/dist/client" +[functions] +# oxfmt resolves native bindings dynamically (@oxfmt/binding-*). +# Keep runtime bindings explicit for Linux glibc targets. +external_node_modules = ["oxfmt", "@oxfmt/binding-linux-x64-gnu"] +included_files = [ + "node_modules/@oxfmt/binding-linux-x64-gnu/**", + "node_modules/.pnpm/@oxfmt+binding-linux-x64-gnu@*/node_modules/@oxfmt/binding-linux-x64-gnu/**", + "website/node_modules/@oxfmt/binding-linux-x64-gnu/**", + "website/node_modules/.pnpm/@oxfmt+binding-linux-x64-gnu@*/node_modules/@oxfmt/binding-linux-x64-gnu/**", +] + [build.environment] NODE_VERSION = "24" AWS_LAMBDA_JS_RUNTIME = "nodejs24.x" diff --git a/website/package.json b/website/package.json index 74021f006..26c13b604 100644 --- a/website/package.json +++ b/website/package.json @@ -39,6 +39,7 @@ "dedent": "catalog:", "mutative": "^1.3.0", "normalize.css": "^8.0.1", + "oxfmt": "catalog:", "prismjs": "^1.30.0", "rad-event-listeners": "^1.1.0", "react": "catalog:", @@ -103,6 +104,9 @@ "vitest-browser-react": "^2.2.0", "workbox-window": "^7.4.1" }, + "optionalDependencies": { + "@oxfmt/binding-linux-x64-gnu": "catalog:" + }, "msw": { "workerDirectory": [ "public" diff --git a/website/src/routes/__root.tsx b/website/src/routes/__root.tsx index ce0af8ddf..40438ea48 100644 --- a/website/src/routes/__root.tsx +++ b/website/src/routes/__root.tsx @@ -15,12 +15,18 @@ import { PreferencesDialog } from "#/shared/components/dialog/pref"; import { Footer } from "#/shared/components/footer"; import { Header } from "#/shared/components/header"; import * as mdxComponents from "#/shared/components/mdx"; -import { NpmSiteProvider, StyleProvider, ThemeProvider } from "#/shared/components/prefs/provider"; +import { + NpmSiteProvider, + StyleProvider, + ThemeProvider, + LigatureProvider, +} from "#/shared/components/prefs/provider"; import { ScrollToTop } from "#/shared/components/scroll-to-top"; import { Sidebar } from "#/shared/components/sidebar"; import { SidebarProvider } from "#/shared/components/sidebar/context"; import { Snackbars } from "#/shared/components/snackbar"; -import { getNpmSiteFn, getStyleFn, getThemeFn } from "#/shared/lib/prefs"; +import { Providers } from "#/shared/components/utils/provider"; +import { getNpmSiteFn, getStyleFn, getThemeFn, getLigatureFn } from "#/shared/lib/prefs"; import { symbolsUrl } from "../../vite/symbols"; @@ -34,7 +40,12 @@ const iconSuffixes = process.env.NODE_ENV === "production" ? ["light", "dark"] : export const Route = createRootRouteWithContext()({ loader: () => - promiseAllKeyed({ theme: getThemeFn(), style: getStyleFn(), npmSite: getNpmSiteFn() }), + promiseAllKeyed({ + theme: getThemeFn(), + style: getStyleFn(), + npmSite: getNpmSiteFn(), + ligature: getLigatureFn(), + }), staticData: { crumb: undefined }, head: () => { @@ -111,38 +122,46 @@ export const Route = createRootRouteWithContext()({ }); function RootDocument({ children }: { children: React.ReactNode }) { - const { theme, style, npmSite } = Route.useLoaderData(); + const { theme, style, npmSite, ligature } = Route.useLoaderData(); useRegisterSW({ immediate: true }); const [prefsOpen, setPrefsOpen] = useState(false); return ( - + - - - - -
- - -
-
setPrefsOpen(true)} /> - -
{children}
-
- - - - setPrefsOpen(false)} /> -
-
-
-
-
-
-
+ +
+ + +
+
setPrefsOpen(true)} /> + +
{children}
+
+ + + + setPrefsOpen(false)} /> +
+
+
+
; const getRawPath = ({ repo = "open-circle/schema-benchmarks", @@ -15,12 +22,26 @@ const getRawPath = ({ fileName, }: RawSpecifier) => `https://raw.githubusercontent.com/${repo}/${branch}/${fileName}`; -export const getRaw = ({ repo, branch, fileName }: RawSpecifier, signalOpt?: AbortSignal) => +const getRawFn = createServerFn() + .validator(rawSpecifierSchema) + .handler(async ({ data: { repo, branch, fileName, formatted } }) => { + const { signal } = getRequest(); + const raw = await upfetch(getRawPath({ repo, branch, fileName }), { + signal, + parseResponse: (response) => response.text(), + }); + return formatted ? getFormattedCodeFn({ data: { fileName, sourceText: raw }, signal }) : raw; + }); + +export const getRaw = ( + { repo, branch, fileName, formatted }: RawSpecifier, + signalOpt?: AbortSignal, +) => queryOptions({ - queryKey: ["raw", repo, branch, fileName], + queryKey: ["raw", repo, branch, fileName, formatted], queryFn: ({ signal }) => - upfetch(getRawPath({ repo, branch, fileName }), { + getRawFn({ + data: { repo, branch, fileName, formatted }, signal: anyAbortSignal(signal, signalOpt), - parseResponse: (response) => response.text(), }), }); diff --git a/website/src/routes/repo/raw.$.tsx b/website/src/routes/repo/raw.$.tsx index 1f1aec832..1b00fd9a4 100644 --- a/website/src/routes/repo/raw.$.tsx +++ b/website/src/routes/repo/raw.$.tsx @@ -1,8 +1,11 @@ import { useSuspenseQuery } from "@tanstack/react-query"; -import { createFileRoute, notFound } from "@tanstack/react-router"; +import { createFileRoute, notFound, stripSearchParams } from "@tanstack/react-router"; import { isResponseError } from "up-fetch"; +import * as v from "valibot"; +import { InternalLinkToggleButton } from "#/shared/components/button/toggle"; import { CodeBlock } from "#/shared/components/code"; +import { MdSymbol } from "#/shared/components/symbol"; import { generateMetadata } from "#/shared/data/meta"; import { getHighlightedCode } from "#/shared/lib/highlight"; @@ -40,13 +43,43 @@ function getLibraryCrumbs(fileName: string) { return [`${libraryName}${note ? ` (${note})` : ""}`, fileNameMapped]; } +const searchSchema = v.object({ + formatted: v.optional( + v.union([ + v.boolean(), + v.pipe( + v.picklist(["true", "false"]), + v.transform((b) => b === "true"), + ), + ]), + false, + ), +}); + export const Route = createFileRoute("/repo/raw/$")({ component: RouteComponent, + validateSearch: searchSchema, + search: { + middlewares: [ + stripSearchParams({ + ...v.getFallbacks(searchSchema), + ...v.getDefaults(searchSchema), + }), + ], + }, staticData: { crumb: undefined }, - async loader({ context: { queryClient }, params: { _splat: fileName }, abortController }) { + loaderDeps: ({ search: { formatted } }) => ({ formatted }), + async loader({ + context: { queryClient }, + params: { _splat: fileName }, + deps: { formatted }, + abortController, + }) { if (!fileName) throw notFound(); try { - const code = await queryClient.ensureQueryData(getRaw({ fileName }, abortController.signal)); + const code = await queryClient.ensureQueryData( + getRaw({ fileName, formatted }, abortController.signal), + ); await queryClient.prefetchQuery( getHighlightedCode({ code, language: getLanguage(fileName) }, abortController.signal), ); @@ -71,9 +104,28 @@ export const Route = createFileRoute("/repo/raw/$")({ function RouteComponent() { const { _splat: fileName = "" } = Route.useParams(); - const { data } = useSuspenseQuery(getRaw({ fileName })); + const { formatted } = Route.useSearch(); + const { data } = useSuspenseQuery(getRaw({ fileName, formatted })); + const isCompiled = fileName.includes("_compiled"); return ( - + ({ formatted: !formatted })} + tooltip={formatted ? "View Raw" : "View Formatted"} + > + {formatted ? "code_off" : "code"} + + ) + } + > {data} ); diff --git a/website/src/shared/components/code/index.tsx b/website/src/shared/components/code/index.tsx index 82185471d..513a3087d 100644 --- a/website/src/shared/components/code/index.tsx +++ b/website/src/shared/components/code/index.tsx @@ -27,7 +27,8 @@ export function InlineCode({ children, language = defaultLanguage, lineNumbers } export interface CodeProps extends InlineCodeProps { title?: string; - copy?: boolean; + showCopy?: boolean; + actions?: ReactNode; } export function CodeBlockContainer({ @@ -35,28 +36,34 @@ export function CodeBlockContainer({ title, lineNumbers = false, language = defaultLanguage, - copy, + showCopy, raw, + actions, }: Override) { return (
-      {(title || copy) && (
+      {(title || showCopy || actions) && (
         
{title} - {copy && ( - { - navigator.clipboard.writeText(raw).then( - () => toastWithHaptics.success("Copied code to clipboard"), - () => toastWithHaptics.error("Failed to copy"), - ); - }} - > - content_copy - - )} + {actions || showCopy ? ( +
+ {actions} + {showCopy && ( + { + navigator.clipboard.writeText(raw).then( + () => toastWithHaptics.success("Copied code to clipboard"), + () => toastWithHaptics.error("Failed to copy"), + ); + }} + > + content_copy + + )} +
+ ) : null}
)} {children} diff --git a/website/src/shared/components/dialog/pref.tsx b/website/src/shared/components/dialog/pref.tsx index 3f24e2824..8e66a88f5 100644 --- a/website/src/shared/components/dialog/pref.tsx +++ b/website/src/shared/components/dialog/pref.tsx @@ -3,7 +3,7 @@ import type { ReactNode } from "react"; import bem from "react-bem-helper"; import { Button, ButtonGroup } from "#/shared/components/button"; -import { useNpmSite, useStyle, useTheme } from "#/shared/components/prefs/context"; +import { useNpmSite, useStyle, useTheme, useLigature } from "#/shared/components/prefs/context"; import { MdSymbol } from "#/shared/components/symbol"; import { useIdDefault } from "#/shared/hooks/use-id-default"; import { @@ -27,6 +27,7 @@ export function PreferencesDialog({ open, onClose }: PreferencesDialogProps) { const { style, setStyle } = useStyle(); const { theme, setTheme } = useTheme(); const { npmSite, setNpmSite } = useNpmSite(); + const { ligature, setLigature } = useLigature(); return ( @@ -55,6 +56,30 @@ export function PreferencesDialog({ open, onClose }: PreferencesDialogProps) { selected={npmSite} onChange={setNpmSite} /> + + => + + ), + }, + false: { + label: "Off", + icon: ( + + => + + ), + }, + }} + title="Code ligatures" + selected={ligature} + onChange={setLigature} + /> @@ -65,7 +90,7 @@ interface PrefGroupProps { title: ReactNode; titleId?: string; options: ReadonlyArray; - labels: Record; + labels: Record; selected: TOpt; onChange: (opt: TOpt) => void; } @@ -86,7 +111,13 @@ function PrefGroup({ {options.map((opt) => (