diff --git a/.changeset/ultracite-8-formatting.md b/.changeset/ultracite-8-formatting.md
new file mode 100644
index 0000000..2ce4c2d
--- /dev/null
+++ b/.changeset/ultracite-8-formatting.md
@@ -0,0 +1,4 @@
+---
+---
+
+Formatting only: apply the blank-line spacing that ultracite 8 enforces and write the Redis field pattern with `\w`. No change in behaviour, so no release.
diff --git a/apps/web/app/changelog/rss.xml/route.ts b/apps/web/app/changelog/rss.xml/route.ts
index 240f3e6..0fc6400 100644
--- a/apps/web/app/changelog/rss.xml/route.ts
+++ b/apps/web/app/changelog/rss.xml/route.ts
@@ -12,6 +12,7 @@ const escapeXml = (value: string): string =>
const handler = () => {
const entries = getChangelogEntries();
+
const items = entries
.map(
(entry) => ` -
@@ -23,6 +24,7 @@ const handler = () => {
`
)
.join("\n");
+
const lastBuildDate = entries[0]?.data.date.toUTCString() ?? "";
const feed = `
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index a08b6a2..f104fb2 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -8,6 +8,7 @@ import { fonts } from "@/lib/fonts";
import { url } from "@/lib/url";
const siteName = "VecStore SDK";
+
const siteDescription =
"A unified vector store SDK for TypeScript. One filter language across Qdrant, pgvector, Pinecone, Supabase, Upstash Vector, Cloudflare Vectorize, and Redis, so you can switch providers without rewriting queries.";
diff --git a/apps/web/app/llms.txt/route.ts b/apps/web/app/llms.txt/route.ts
index 9cdf0fc..3e37531 100644
--- a/apps/web/app/llms.txt/route.ts
+++ b/apps/web/app/llms.txt/route.ts
@@ -5,6 +5,7 @@ export const revalidate = false;
const handler = async () => {
const pages = await Promise.all(source.getPages().map(getLLMText));
+
return new Response(pages.join("\n\n"), {
headers: { "Content-Type": "text/plain" },
});
diff --git a/apps/web/app/og/docs/[...slug]/route.ts b/apps/web/app/og/docs/[...slug]/route.ts
index a97ece4..55bfc61 100644
--- a/apps/web/app/og/docs/[...slug]/route.ts
+++ b/apps/web/app/og/docs/[...slug]/route.ts
@@ -13,10 +13,12 @@ const backgrounds = path.join(process.cwd(), "app", "og", "docs", "[...slug]");
const readBackground = (slugs: readonly string[]) => {
const [group, name] = slugs;
+
const provider =
group === "providers" && providerIds.some((id) => id === name)
? name
: undefined;
+
const file = provider ? `background-${provider}.png` : "background.png";
return readFile(path.join(backgrounds, file));
diff --git a/apps/web/components/brand-menu.tsx b/apps/web/components/brand-menu.tsx
index 528c3f3..c6530fe 100644
--- a/apps/web/components/brand-menu.tsx
+++ b/apps/web/components/brand-menu.tsx
@@ -15,15 +15,19 @@ const MENU_GAP = 8;
const serialize = (svg: SVGSVGElement): string => {
const clone = svg.cloneNode(true);
+
if (!(clone instanceof SVGSVGElement)) {
return "";
}
+
const ink = document.documentElement.classList.contains("dark")
? "#ededed"
: "#171717";
+
clone.removeAttribute("class");
clone.removeAttribute("aria-hidden");
clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
+
return new XMLSerializer()
.serializeToString(clone)
.replaceAll("currentColor", ink);
@@ -44,7 +48,9 @@ const CopyTile = ({
if (status === "idle") {
return;
}
+
const timer = window.setTimeout(() => setStatus("idle"), COPIED_MS);
+
return () => window.clearTimeout(timer);
}, [status]);
@@ -53,9 +59,11 @@ const CopyTile = ({
className="text-gray-1000 hover:bg-gray-alpha-100 flex min-w-0 flex-col gap-2 rounded-md p-2 text-left text-[14px] transition-colors"
onClick={async (event) => {
event.stopPropagation();
+
if (!svgRef.current) {
return;
}
+
try {
await navigator.clipboard.writeText(serialize(svgRef.current));
setStatus("copied");
@@ -98,18 +106,23 @@ export const BrandMenu = ({ children }: { readonly children: ReactNode }) => {
useEffect(() => {
const anchor = anchorRef.current?.closest("a") ?? anchorRef.current;
+
if (!anchor) {
return;
}
+
anchor.setAttribute("aria-haspopup", "menu");
anchor.setAttribute("aria-controls", menuId);
+
const handle = (event: Event) => {
event.preventDefault();
const rect = anchor.getBoundingClientRect();
setPosition({ left: rect.left, top: rect.bottom + MENU_GAP });
setOpen(true);
};
+
anchor.addEventListener("contextmenu", handle);
+
return () => anchor.removeEventListener("contextmenu", handle);
}, [menuId]);
@@ -122,11 +135,14 @@ export const BrandMenu = ({ children }: { readonly children: ReactNode }) => {
if (!open) {
return;
}
+
menuRef.current?.querySelector("[role=menuitem]")?.focus();
+
const close = (event: Event) => {
if (event instanceof KeyboardEvent && event.key !== "Escape") {
return;
}
+
if (
event instanceof PointerEvent &&
event.target instanceof Node &&
@@ -134,11 +150,14 @@ export const BrandMenu = ({ children }: { readonly children: ReactNode }) => {
) {
return;
}
+
setOpen(false);
(anchorRef.current?.closest("a") ?? anchorRef.current)?.focus();
};
+
document.addEventListener("keydown", close);
document.addEventListener("pointerdown", close);
+
return () => {
document.removeEventListener("keydown", close);
document.removeEventListener("pointerdown", close);
@@ -148,34 +167,44 @@ export const BrandMenu = ({ children }: { readonly children: ReactNode }) => {
const moveFocus = (event: KeyboardEvent) => {
if (event.key === "Tab") {
setOpen(false);
+
return;
}
+
const items = [
...(menuRef.current?.querySelectorAll("[role=menuitem]") ??
[]),
];
+
const active = document.activeElement;
const index = active instanceof HTMLElement ? items.indexOf(active) : -1;
+
const resolveTarget = (): HTMLElement | undefined => {
switch (event.key) {
case "ArrowDown": {
return items.at((index + 1) % items.length);
}
+
case "ArrowUp": {
return items.at((index - 1 + items.length) % items.length);
}
+
case "End": {
return items.at(-1);
}
+
case "Home": {
return items[0];
}
+
default: {
return undefined;
}
}
};
+
const target = resolveTarget();
+
if (target) {
event.preventDefault();
target.focus();
diff --git a/apps/web/components/docs/sidebar-items.tsx b/apps/web/components/docs/sidebar-items.tsx
index a46869c..dcee151 100644
--- a/apps/web/components/docs/sidebar-items.tsx
+++ b/apps/web/components/docs/sidebar-items.tsx
@@ -24,16 +24,19 @@ const containsPath = (
if (node.type === "page") {
return node.url === pathname;
}
+
if (node.type === "folder") {
return (
node.index?.url === pathname || containsPath(node.children, pathname)
);
}
+
return false;
});
export const Item = ({ item }: { readonly item: PageTreeItem }) => {
const pathname = usePathname();
+
return (
navigator.clipboard.writeText(command)
);
+
const Icon = copied ? CheckIcon : CopyIcon;
return (
diff --git a/apps/web/components/landing/demo.tsx b/apps/web/components/landing/demo.tsx
index 44b5195..55b94e0 100644
--- a/apps/web/components/landing/demo.tsx
+++ b/apps/web/components/landing/demo.tsx
@@ -47,6 +47,7 @@ export const Demo = async () => {
await renderCode(provider.snippet(example.filterCode)),
])
);
+
return {
code: Object.fromEntries(entries),
expression: example.expression,
@@ -56,6 +57,7 @@ export const Demo = async () => {
};
})
);
+
const providers = demoProviders.map(({ id, label }) => ({ id, label }));
return ;
diff --git a/apps/web/components/landing/hero-demo.tsx b/apps/web/components/landing/hero-demo.tsx
index e6c6ca1..fd1628f 100644
--- a/apps/web/components/landing/hero-demo.tsx
+++ b/apps/web/components/landing/hero-demo.tsx
@@ -52,15 +52,19 @@ export const HeroDemo = ({ providers, examples }: HeroDemoProps) => {
useLayoutEffect(() => {
const measure = () => {
const tab = tabRefs.current[exampleIndex];
+
if (tab) {
setIndicator({ left: tab.offsetLeft, width: tab.offsetWidth });
}
};
+
measure();
const observer = new ResizeObserver(measure);
+
if (listRef.current) {
observer.observe(listRef.current);
}
+
return () => observer.disconnect();
}, [exampleIndex]);
const example = examples[exampleIndex] ?? examples[0];
@@ -77,6 +81,7 @@ export const HeroDemo = ({ providers, examples }: HeroDemoProps) => {
const selectProvider = (id: string) => {
const index = providers.findIndex((item) => item.id === id);
+
if (index !== -1) {
setProviderIndex(index);
}
@@ -103,6 +108,7 @@ export const HeroDemo = ({ providers, examples }: HeroDemoProps) => {
/>
{examples.map((item, index) => {
const active = index === exampleIndex;
+
return (