From 39db346178b011e5addca584dbc89e836220faa0 Mon Sep 17 00:00:00 2001 From: hmziqrs Date: Wed, 1 Jul 2026 17:49:19 +0500 Subject: [PATCH 01/22] chore: move to freeoxide org and gpui-query.freeoxide.com Repository transferred hmziqrs/gpui-query -> freeoxide/gpui-query and the site is moving to gpui-query.freeoxide.com. Update all repo-URL and domain references (Cargo.toml homepage/repository, READMEs, CHANGELOG, web/ SEO metadata, robots.txt, llms.txt generator, sitemap host, Docusaurus config). Author attribution (authors, x.com/hmziqrs, hmziq.rs) left unchanged. --- CHANGELOG.md | 10 +++++----- README.md | 6 +++--- crates/gpui-query-http/Cargo.toml | 4 ++-- crates/gpui-query-legacy/Cargo.toml | 4 ++-- crates/gpui-query-legacy/README.md | 2 +- crates/gpui-query-legacy/src/lib.rs | 4 ++-- crates/gpui-query-persist/Cargo.toml | 4 ++-- crates/gpui-query/Cargo.toml | 4 ++-- crates/gpui-query/README.md | 6 +++--- justfile | 2 +- web/public/robots.txt | 6 +++--- web/scripts/generate-llms-txt.mjs | 6 +++--- web/src/components/footer.tsx | 2 +- web/src/components/landing/cta-section.tsx | 2 +- web/src/components/landing/hero-section.tsx | 2 +- web/src/components/mobile-nav.tsx | 2 +- web/src/components/navbar.tsx | 2 +- web/src/content/blog/introducing-gpui-query.tsx | 2 +- web/src/routes/about.tsx | 16 ++++++++++------ web/src/routes/blog/$slug.tsx | 2 +- web/src/routes/blog/index.tsx | 6 +++--- web/src/routes/changelog.tsx | 6 +++--- web/src/routes/faq.tsx | 8 ++++---- web/src/routes/index.tsx | 12 ++++++------ web/vite.config.ts | 2 +- website/docusaurus.config.ts | 6 +++--- 26 files changed, 66 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ccb85..80452ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,8 +101,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Initial public release -[0.1.4]: https://github.com/hmziqrs/gpui-query/releases/tag/v0.1.4 -[0.1.3]: https://github.com/hmziqrs/gpui-query/releases/tag/v0.1.3 -[0.1.2]: https://github.com/hmziqrs/gpui-query/releases/tag/v0.1.2 -[0.1.1]: https://github.com/hmziqrs/gpui-query/releases/tag/v0.1.1 -[0.1.0]: https://github.com/hmziqrs/gpui-query/releases/tag/v0.1.0 +[0.1.4]: https://github.com/freeoxide/gpui-query/releases/tag/v0.1.4 +[0.1.3]: https://github.com/freeoxide/gpui-query/releases/tag/v0.1.3 +[0.1.2]: https://github.com/freeoxide/gpui-query/releases/tag/v0.1.2 +[0.1.1]: https://github.com/freeoxide/gpui-query/releases/tag/v0.1.1 +[0.1.0]: https://github.com/freeoxide/gpui-query/releases/tag/v0.1.0 diff --git a/README.md b/README.md index c28d16d..067d4a5 100644 --- a/README.md +++ b/README.md @@ -252,9 +252,9 @@ Garbage collection runs on idle resources older than `gc_time_ms` (default: 5 mi ## links -- Documentation: -- Website: -- Source: +- Documentation: +- Website: +- Source: - GPUI framework: - TanStack Query: diff --git a/crates/gpui-query-http/Cargo.toml b/crates/gpui-query-http/Cargo.toml index b058a73..d53a3a1 100644 --- a/crates/gpui-query-http/Cargo.toml +++ b/crates/gpui-query-http/Cargo.toml @@ -3,11 +3,11 @@ name = "gpui-query-http" version = "0.1.0" edition = "2024" description = "HTTP cache-header helpers for gpui-query (server-wins CachePolicy + HttpCache)" -repository = "https://github.com/hmziqrs/gpui-query" +repository = "https://github.com/freeoxide/gpui-query" license = "MIT" keywords = ["gpui", "http", "caching", "cache-control", "query"] categories = ["asynchronous", "caching", "web-programming::http-client"] -homepage = "https://gpui-query.hmziq.xyz" +homepage = "https://gpui-query.freeoxide.com" authors = ["hmziqrs"] [features] diff --git a/crates/gpui-query-legacy/Cargo.toml b/crates/gpui-query-legacy/Cargo.toml index 7dd2de1..4ba5ee3 100644 --- a/crates/gpui-query-legacy/Cargo.toml +++ b/crates/gpui-query-legacy/Cargo.toml @@ -3,11 +3,11 @@ name = "gpui-query-legacy" version = "0.1.4" edition = "2024" description = "TanStack Query-inspired async state management for GPUI (legacy, deprecated)" -repository = "https://github.com/hmziqrs/gpui-query" +repository = "https://github.com/freeoxide/gpui-query" license = "MIT" keywords = ["gpui", "async", "state-management", "query", "zed"] categories = ["asynchronous", "caching", "gui"] -homepage = "https://gpui-query.hmziq.xyz" +homepage = "https://gpui-query.freeoxide.com" authors = ["hmziqrs"] readme = "README.md" diff --git a/crates/gpui-query-legacy/README.md b/crates/gpui-query-legacy/README.md index 7412ced..fe91adf 100644 --- a/crates/gpui-query-legacy/README.md +++ b/crates/gpui-query-legacy/README.md @@ -12,7 +12,7 @@ Use **gpui-query** (v2) instead. The v2 crate provides: - **Fixed retry loops** and signal lifecycle - **Efficient re-renders** with status deduplication -See the migration guide at [https://gpui-query.hmziq.xyz/docs/](https://gpui-query.hmziq.xyz/docs/). +See the migration guide at [https://gpui-query.freeoxide.com/docs/](https://gpui-query.freeoxide.com/docs/). ## Author diff --git a/crates/gpui-query-legacy/src/lib.rs b/crates/gpui-query-legacy/src/lib.rs index 60e3edb..1ee972a 100644 --- a/crates/gpui-query-legacy/src/lib.rs +++ b/crates/gpui-query-legacy/src/lib.rs @@ -8,10 +8,10 @@ //! - Fixed retry loops and signal lifecycle //! - Efficient re-renders with status deduplication //! -//! See https://gpui-query.hmziq.xyz/docs/ for migration guidance. +//! See https://gpui-query.freeoxide.com/docs/ for migration guidance. #![deprecated( since = "0.1.0", - note = "gpui-query-legacy is deprecated. Use gpui-query (v2) instead. See https://gpui-query.hmziq.xyz/docs/ for migration guidance." + note = "gpui-query-legacy is deprecated. Use gpui-query (v2) instead. See https://gpui-query.freeoxide.com/docs/ for migration guidance." )] #[cfg(feature = "core")] diff --git a/crates/gpui-query-persist/Cargo.toml b/crates/gpui-query-persist/Cargo.toml index 37dd231..bc990d7 100644 --- a/crates/gpui-query-persist/Cargo.toml +++ b/crates/gpui-query-persist/Cargo.toml @@ -3,11 +3,11 @@ name = "gpui-query-persist" version = "0.1.0" edition = "2024" description = "Reference disk-based persistence adapter (FilePersister) for gpui-query" -repository = "https://github.com/hmziqrs/gpui-query" +repository = "https://github.com/freeoxide/gpui-query" license = "MIT" keywords = ["gpui", "persistence", "cache", "query", "filesystem"] categories = ["asynchronous", "caching", "filesystem"] -homepage = "https://gpui-query.hmziq.xyz" +homepage = "https://gpui-query.freeoxide.com" authors = ["hmziqrs"] [features] diff --git a/crates/gpui-query/Cargo.toml b/crates/gpui-query/Cargo.toml index 3ccd3b0..36e2d00 100644 --- a/crates/gpui-query/Cargo.toml +++ b/crates/gpui-query/Cargo.toml @@ -3,12 +3,12 @@ name = "gpui-query" version = "0.2.0" edition = "2024" description = "TanStack Query-inspired async state management for GPUI" -repository = "https://github.com/hmziqrs/gpui-query" +repository = "https://github.com/freeoxide/gpui-query" readme = "README.md" license = "MIT" keywords = ["gpui", "async", "state-management", "query", "zed"] categories = ["asynchronous", "caching", "gui"] -homepage = "https://gpui-query.hmziq.xyz" +homepage = "https://gpui-query.freeoxide.com" authors = ["hmziqrs"] [features] diff --git a/crates/gpui-query/README.md b/crates/gpui-query/README.md index dab45c2..73fab8e 100644 --- a/crates/gpui-query/README.md +++ b/crates/gpui-query/README.md @@ -98,9 +98,9 @@ The crate is split into three layers, each behind a feature flag: ## Links -- Website: -- Docs: -- Source: +- Website: +- Docs: +- Source: - GPUI: - TanStack Query: diff --git a/justfile b/justfile index 784af72..954c326 100644 --- a/justfile +++ b/justfile @@ -67,7 +67,7 @@ ci-logs run_id: # Open the GitHub releases page releases: - open "https://github.com/hmziqrs/gpui-query/releases" + open "https://github.com/freeoxide/gpui-query/releases" # Create a new release: bumps version in CHANGELOG, commits, and pushes # Usage: just release 0.1.2 diff --git a/web/public/robots.txt b/web/public/robots.txt index b2d27ce..c68be37 100644 --- a/web/public/robots.txt +++ b/web/public/robots.txt @@ -1,8 +1,8 @@ User-agent: * Allow: / -Sitemap: https://gpui-query.hmziq.xyz/sitemap.xml +Sitemap: https://gpui-query.freeoxide.com/sitemap.xml # AI agent files -# llms.txt: https://gpui-query.hmziq.xyz/llms.txt -# llms-full.txt: https://gpui-query.hmziq.xyz/llms-full.txt +# llms.txt: https://gpui-query.freeoxide.com/llms.txt +# llms-full.txt: https://gpui-query.freeoxide.com/llms-full.txt diff --git a/web/scripts/generate-llms-txt.mjs b/web/scripts/generate-llms-txt.mjs index fab1b79..e70fd19 100644 --- a/web/scripts/generate-llms-txt.mjs +++ b/web/scripts/generate-llms-txt.mjs @@ -18,7 +18,7 @@ const outputDir = process.argv.includes("--output") // Scripts run from web/, so the Docusaurus docs live one level up. const docsRoot = resolve("..", "website", "docs"); -const siteUrl = "https://gpui-query.hmziq.xyz"; +const siteUrl = "https://gpui-query.freeoxide.com"; /** * Recursively collect every .md and .mdx file under `dir`. @@ -180,9 +180,9 @@ async function main() { llmsLines.push(""); llmsLines.push("## Links"); llmsLines.push(""); - llmsLines.push("- [GitHub](https://github.com/hmziqrs/gpui-query): Source code and issues"); + llmsLines.push("- [GitHub](https://github.com/freeoxide/gpui-query): Source code and issues"); llmsLines.push( - "- [Introduction](https://gpui-query.hmziq.xyz/docs/): What is gpui-query and why you need it", + "- [Introduction](https://gpui-query.freeoxide.com/docs/): What is gpui-query and why you need it", ); const llmsTxt = llmsLines.join("\n"); diff --git a/web/src/components/footer.tsx b/web/src/components/footer.tsx index 1745e1e..1dbfdc9 100644 --- a/web/src/components/footer.tsx +++ b/web/src/components/footer.tsx @@ -58,7 +58,7 @@ export function Footer() { @@ -93,7 +93,30 @@ export function Footer() {
  • - MIT License + + Privacy Policy + +
  • +
  • + + Terms of Service + +
  • +
  • + + MIT License +
diff --git a/web/src/components/mobile-nav.tsx b/web/src/components/mobile-nav.tsx index c418a68..5da6f2b 100644 --- a/web/src/components/mobile-nav.tsx +++ b/web/src/components/mobile-nav.tsx @@ -6,7 +6,6 @@ const navLinks = [ { href: "/docs/", label: "Docs" }, { href: "/blog", label: "Blog" }, { href: "/faq", label: "FAQ" }, - { href: "/about", label: "About" }, ]; interface MobileNavProps { diff --git a/web/src/content/blog/introducing-gpui-query.tsx b/web/src/content/blog/introducing-gpui-query.tsx deleted file mode 100644 index 46fdc4f..0000000 --- a/web/src/content/blog/introducing-gpui-query.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { CodeBlock } from "#/components/code-block"; - -/** - * Body for the "Introducing gpui-query" post. Frontmatter lives in - * `web/src/lib/blog.ts`. - */ -export default function IntroducingGpuiQueryPost() { - return ( - <> -

- gpui-query brings the battle-tested patterns of{" "} - - TanStack Query - {" "} - to the{" "} - - GPUI - {" "} - framework. If you've ever wrestled with loading states, caching, or race conditions in - your GPUI app — this is for you. -

- -

Why gpui-query?

-

- Building async-driven UIs in GPUI meant writing repetitive boilerplate: manual fetch logic, - cache management, stale-while-revalidate heuristics, and cooperative cancellation.{" "} - gpui-query handles all of this out of the box. -

- - -
-          {`let (data, status) = use_query(cx, || QueryArgs::new(
-    "users".into(),
-    || async { fetch_users().await },
-));`}
-        
-
- -

- That's it. Automatic caching, background refetching, retry with exponential backoff — - all configured by default. -

- -

What's included

-
    -
  • - Queries (use_query) — declarative async data fetching with - caching -
  • -
  • - Mutations (use_mutation) — server-side modifications with - success/error callbacks -
  • -
  • - Infinite Queries (use_infinite_query) — paginated data with - bidirectional fetching -
  • -
  • - Query Observers — fine-grained subscriptions for derived data -
  • -
  • - Persistence API — custom serialization backends for offline support -
  • -
  • - DevTools — diagnostic types for inspecting cache state -
  • -
- -

What's next

-

- We're working on DevTools UI integration, server-state synchronization patterns, and a - plugin system for custom query backends.{" "} - - Star the repo - {" "} - to follow along. -

- - ); -} diff --git a/web/src/lib/blog.ts b/web/src/lib/blog.ts index d09ac1d..3f9ef25 100644 --- a/web/src/lib/blog.ts +++ b/web/src/lib/blog.ts @@ -1,6 +1,5 @@ import { type ComponentType } from "react"; -import IntroducingGpuiQueryPost from "../content/blog/introducing-gpui-query"; import WhyGpuiQueryPost from "../content/blog/why-gpui-query"; import CooperativeCancellationPost from "../content/blog/cooperative-cancellation"; import CachePoliciesExplainedPost from "../content/blog/cache-policies-explained"; @@ -32,18 +31,6 @@ export interface BlogPost { * problem. Frontmatter lives here alongside the component import. */ const POSTS: BlogPost[] = [ - { - slug: "introducing-gpui-query", - Content: IntroducingGpuiQueryPost, - frontmatter: { - title: "Introducing gpui-query", - description: - "Zero-boilerplate async state management for GPUI — inspired by TanStack Query, built for Rust.", - date: "2026-03-10", - author: "hmziqrs", - tags: ["gpui", "rust", "async", "introduction"], - }, - }, { slug: "why-gpui-query", Content: WhyGpuiQueryPost, diff --git a/web/src/routes/about.tsx b/web/src/routes/about.tsx deleted file mode 100644 index c7638d5..0000000 --- a/web/src/routes/about.tsx +++ /dev/null @@ -1,465 +0,0 @@ -import { createFileRoute, Link } from "@tanstack/react-router"; -import { - GithubLogoIcon, - ArrowUpRightIcon, - StackIcon, - BookOpenIcon, - CpuIcon, - DatabaseIcon, - CodeIcon, - PackageIcon, - ShieldCheckIcon, - FileTextIcon, - NotePencilIcon, - UserIcon, - HeartIcon, - GlobeIcon, - XLogoIcon, - LightningIcon, -} from "@phosphor-icons/react"; -import { Button } from "#/components/ui/button"; -import { Card, CardHeader, CardContent } from "#/components/ui/card"; -import { Badge } from "#/components/ui/badge"; -import { Separator } from "#/components/ui/separator"; - -export const Route = createFileRoute("/about")({ - head: () => ({ - meta: [ - { title: "About - gpui-query" }, - { - name: "description", - content: - "About gpui-query — zero-boilerplate async state management for the GPUI framework.", - }, - { property: "og:title", content: "About - gpui-query" }, - { - property: "og:description", - content: - "About gpui-query — zero-boilerplate async state management for the GPUI framework.", - }, - { property: "og:type", content: "website" }, - { property: "og:image", content: "https://gpui-query.freeoxide.com/og-image.png" }, - { name: "twitter:card", content: "summary_large_image" }, - { name: "twitter:title", content: "About - gpui-query" }, - { - name: "twitter:description", - content: - "About gpui-query — zero-boilerplate async state management for the GPUI framework.", - }, - { name: "twitter:image", content: "https://gpui-query.freeoxide.com/og-image.png" }, - ], - links: [{ rel: "canonical", href: "https://gpui-query.freeoxide.com/about" }], - }), - component: AboutPage, -}); - -function AboutPage() { - return ( -
- - - - - - -
- ); -} - -/* ─── Hero ─────────────────────────────────────────────────────── */ - -function HeroSection() { - return ( -
-
-
-
- - Built with care for the Rust ecosystem -
-

- About gpui-query -

-

- The story behind async state management for GPUI -

-
-
-
- ); -} - -/* ─── What Is ──────────────────────────────────────────────────── */ - -function WhatIsSection() { - return ( -
-
-
- - -
-
- -
-

What is gpui-query?

-
-
- -

- gpui-query brings the reactive query - patterns you love from the web world into Rust GPUI applications. Fetch, cache, and - synchronize async data with a single hook — no manual lifecycle management required. -

-

- Built on a layered architecture with a framework-agnostic Core, a Client layer for - caching and garbage collection, and a Hook layer that integrates directly with - GPUI's reactive primitives, gpui-query is designed to feel natural in the Rust - ecosystem while providing battle-tested patterns from TanStack Query. -

-

- Whether you are building plugins for the{" "} - - Zed editor - {" "} - or crafting your own GPUI application, gpui-query eliminates the boilerplate of - managing asynchronous data so you can focus on shipping features. -

-
-
-
-
-
- ); -} - -/* ─── Author ───────────────────────────────────────────────────── */ - -function AuthorSection() { - return ( -
-
-
- - -
-
- -
-

The Author

-
-
- -
- {/* Avatar */} -
- H -
-
-
-

hmziq

-

- Rust developer & open-source contributor -

-
-

- Created and maintains gpui-query. Passionate about building ergonomic developer - tools that make complex async patterns accessible to everyone in the Rust - ecosystem. -

- {/* Author links */} - -
-
-
-
- - {/* Related projects */} - -
-
-
- ); -} - -/* ─── Tech Stack ───────────────────────────────────────────────── */ - -const techStack = [ - { - icon: CpuIcon, - label: "GPUI", - description: "Zed's GPU-accelerated UI framework", - }, - { - icon: CodeIcon, - label: "Rust", - description: "Memory-safe, blazing fast", - }, - { - icon: DatabaseIcon, - label: "Query Cache", - description: "TTL, SWR, LatestWins policies", - }, - { - icon: StackIcon, - label: "3-Layer Arch", - description: "Core, Client, Hook separation", - }, - { - icon: ShieldCheckIcon, - label: "Type Safe", - description: "Compile-time guarantees", - }, - { - icon: PackageIcon, - label: "Crate.io", - description: "Published Rust package", - }, -]; - -function TechStackSection() { - return ( -
-
-
-

Technology Stack

-

- Built with proven technologies and patterns for reliable async state management. -

-
- -
- {techStack.map((tech) => ( -
-
- -
-

{tech.label}

-

{tech.description}

-
- ))} -
-
-
- ); -} - -/* ─── Open Source ──────────────────────────────────────────────── */ - -function OpenSourceSection() { - return ( -
-
-
- - -
-
- -
-

Open Source

-
-
- -
- - MIT License - - - Free to use, modify, and distribute - -
- -

- gpui-query is open-source software released under the{" "} - MIT License. You are free to use it in - personal projects, commercial applications, and anything in between. Contributions, - bug reports, and feature requests are always welcome on GitHub. -

- -
-
-
-
-
- ); -} - -/* ─── Links ────────────────────────────────────────────────────── */ - -function LinkCard({ - icon: Icon, - label, - description, -}: { - icon: React.ComponentType<{ size?: number; className?: string }>; - label: string; - description: string; -}) { - return ( -
-
- -
-
-

- {label} - -

-

{description}

-
-
- ); -} - -function LinksSection() { - return ( -
-
-
-

Explore More

-

- Dive deeper into gpui-query with these resources. -

-
- -
- - - - - - - - - - - - - - - -
-
-
- ); -} diff --git a/web/src/routes/privacy.tsx b/web/src/routes/privacy.tsx new file mode 100644 index 0000000..27622f1 --- /dev/null +++ b/web/src/routes/privacy.tsx @@ -0,0 +1,133 @@ +import { createFileRoute } from "@tanstack/react-router"; + +const title = "Privacy Policy - gpui-query"; +const description = + "How the gpui-query website handles data: Firebase Analytics, Google Fonts, Cloudflare hosting, and local storage."; +const url = "https://gpui-query.freeoxide.com/privacy"; + +export const Route = createFileRoute("/privacy")({ + head: () => ({ + meta: [ + { title }, + { name: "description", content: description }, + { property: "og:title", content: title }, + { property: "og:description", content: description }, + { property: "og:type", content: "website" }, + { property: "og:url", content: url }, + ], + links: [{ rel: "canonical", href: url }], + }), + component: PrivacyPage, +}); + +function PrivacyPage() { + return ( +
+
+

Privacy Policy

+

Last updated: July 9, 2026

+
+ +
+

+ This policy covers the gpui-query website at{" "} + gpui-query.freeoxide.com, including the + documentation and blog. The site has no user accounts, no comment forms, and no + newsletter. We do not ask you for personal information anywhere on it. The data described + below is what the services we build on collect. +

+ +

Analytics

+

+ We use Firebase Analytics (part of Google Firebase, operated by Google + LLC) to understand how the site is used — which pages are visited, roughly where visitors + come from, and what kind of device and browser they use. Firebase collects this through a + device identifier and reports it to us only in aggregate. We use it to see which docs + pages get traffic and where readers drop off, nothing else. +

+

+ Google's own use of this data is described in the{" "} + + Firebase privacy documentation + {" "} + and the{" "} + + Google Privacy Policy + + . Most content blockers block Firebase Analytics; the site works exactly the same with it + blocked. +

+ +

Hosting

+

+ The site is served by Cloudflare. Like any web host, Cloudflare sees your + IP address and request details and keeps short-lived server logs for security and + operations. See the{" "} + + Cloudflare Privacy Policy + + . +

+ +

Fonts

+

+ Fonts are loaded from Google Fonts. When your browser fetches a font + file, your IP address is sent to Google as part of that request. Details are in Google's{" "} + + Google Fonts privacy FAQ + + . +

+ +

Local storage

+

+ Your light/dark theme choice is saved in your browser's localStorage. It + never leaves your device and you can clear it at any time through your browser settings. +

+ +

External links

+

+ The site links out to GitHub, crates.io, docs.rs, and other third-party sites. Once you + follow one of those links, that site's privacy policy applies, not this one. +

+ +

Children

+

+ The site is developer documentation and is not directed at children under 13. We do not + knowingly collect any information from them. +

+ +

Changes

+

+ If we change what the site collects — for example by adding or removing a service — we + will update this page and the date at the top. +

+ +

Contact

+

+ Questions about this policy? Open an issue on{" "} + + GitHub + {" "} + or email hmziqrs@gmail.com. +

+
+
+ ); +} diff --git a/web/src/routes/terms.tsx b/web/src/routes/terms.tsx new file mode 100644 index 0000000..4597aec --- /dev/null +++ b/web/src/routes/terms.tsx @@ -0,0 +1,106 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; + +const title = "Terms of Service - gpui-query"; +const description = + "Terms for using the gpui-query website and documentation. The gpui-query library itself is MIT licensed."; +const url = "https://gpui-query.freeoxide.com/terms"; + +export const Route = createFileRoute("/terms")({ + head: () => ({ + meta: [ + { title }, + { name: "description", content: description }, + { property: "og:title", content: title }, + { property: "og:description", content: description }, + { property: "og:type", content: "website" }, + { property: "og:url", content: url }, + ], + links: [{ rel: "canonical", href: url }], + }), + component: TermsPage, +}); + +function TermsPage() { + return ( +
+
+

Terms of Service

+

Last updated: July 9, 2026

+
+ +
+

+ These terms apply to the gpui-query website at{" "} + gpui-query.freeoxide.com — the landing + pages, documentation, blog, and changelog. By using the site you agree to them. They are + short because the site is simple: it is free documentation for an open-source library. +

+ +

The library is MIT licensed

+

+ The gpui-query crate itself is distributed under the{" "} + + MIT License + + . That license — not these terms — governs your use of the source code and the published + crate. You can use it in personal and commercial projects, modify it, and redistribute + it, subject to the license's conditions. +

+ +

Site content

+

+ You may read, link to, and quote the documentation and blog posts with attribution. Code + snippets shown in the docs and blog are provided so you can use them; treat them as + MIT-licensed like the library they document. +

+ +

No warranty

+

+ The site and its content are provided as-is. We work to keep the documentation accurate, + but APIs change between releases and pages can lag behind the code. Nothing on this site + is a guarantee that the library is fit for a particular purpose, and we are not liable + for damages arising from your use of the site or the library — the same disclaimer the + MIT License makes for the code. +

+ +

Acceptable use

+

+ Don't attempt to disrupt the site, scrape it at a rate that degrades it for others, or + use it to distribute malware or spam. That's it. +

+ +

Third-party services and links

+

+ The site links to third-party sites (GitHub, crates.io, docs.rs, and others) and relies + on the services described in the Privacy Policy, including + Firebase Analytics, Google Fonts, and Cloudflare. We don't control those services and + aren't responsible for their content or conduct. +

+ +

Changes

+

+ We may update these terms as the site evolves. Material changes will be reflected on + this page with a new date at the top. Continuing to use the site after a change means + you accept the updated terms. +

+ +

Contact

+

+ Questions about these terms? Open an issue on{" "} + + GitHub + {" "} + or email hmziqrs@gmail.com. +

+
+
+ ); +} From 4168271b0e949d094361098b601d9a8bb1cb98a8 Mon Sep 17 00:00:00 2001 From: hmziqrs Date: Thu, 9 Jul 2026 01:13:23 +0500 Subject: [PATCH 05/22] Migrate blog posts from TSX to MDX and clean up formatting --- crates/gpui-query-http/src/cache.rs | 20 +-- .../tests/file_persister.rs | 36 ++-- crates/gpui-query/src/client/bucket/ops.rs | 4 +- .../gpui-query/src/client/infinite_bucket.rs | 10 +- crates/gpui-query/src/client/mod.rs | 8 +- .../gpui-query/src/client/mutation_bucket.rs | 7 +- .../gpui-query/src/client/prepared_fetch.rs | 6 +- crates/gpui-query/src/tests/core_cache/mod.rs | 4 +- .../tests/core_infinite_query/max_pages.rs | 4 +- .../client_operations/persist_with_hydrate.rs | 42 +++-- .../property_tests/query_key/proptests.rs | 7 +- crates/gpui-query/src/tests/test_support.rs | 1 + {website => docs}/.gitignore | 0 {website => docs}/README.md | 0 .../docs/advanced/_category_.json | 0 .../docs/advanced/api-reference.mdx | 0 .../docs/advanced/comparison.mdx | 0 {website => docs}/docs/advanced/devtools.mdx | 0 {website => docs}/docs/advanced/migration.mdx | 0 {website => docs}/docs/advanced/observers.mdx | 0 {website => docs}/docs/api/_category_.json | 0 .../docs/api/infinite-queries.mdx | 0 {website => docs}/docs/api/mutations.mdx | 0 {website => docs}/docs/api/queries.mdx | 0 {website => docs}/docs/api/query-client.mdx | 0 .../docs/getting-started/_category_.json | 0 .../docs/getting-started/installation.mdx | 0 .../docs/getting-started/quick-start.mdx | 0 {website => docs}/docs/guides/_category_.json | 0 {website => docs}/docs/guides/caching.mdx | 0 .../docs/guides/error-handling.mdx | 0 {website => docs}/docs/guides/persistence.mdx | 0 {website => docs}/docs/guides/query-keys.mdx | 0 {website => docs}/docs/guides/retry.mdx | 0 .../docs/guides/select-pattern.mdx | 0 {website => docs}/docs/intro.mdx | 0 {website => docs}/docusaurus.config.ts | 0 {website => docs}/package-lock.json | 0 {website => docs}/package.json | 0 {website => docs}/sidebars.ts | 0 {website => docs}/src/css/custom.css | 0 {website => docs}/src/theme/Logo/index.tsx | 0 {website => docs}/static/.nojekyll | 0 {website => docs}/static/img/favicon.ico | Bin {website => docs}/static/img/logo.svg | 0 {website => docs}/tsconfig.json | 0 web/.vite-hooks/pre-commit | 1 - web/package.json | 3 +- web/src/components/code-block.tsx | 53 ------ web/src/components/landing/rust-code.tsx | 67 +------ web/src/components/landing/rust-tint.tsx | 79 ++++++++ web/src/components/landing/v3.tsx | 2 +- .../components/landing/version-switcher.tsx | 16 +- .../content/blog/cache-policies-explained.mdx | 79 ++++++++ .../content/blog/cache-policies-explained.tsx | 168 ------------------ .../content/blog/cooperative-cancellation.mdx | 83 +++++++++ .../content/blog/cooperative-cancellation.tsx | 143 --------------- web/src/content/blog/why-gpui-query.mdx | 55 ++++++ web/src/content/blog/why-gpui-query.tsx | 113 ------------ web/src/lib/blog.ts | 79 ++++---- web/src/lib/seo.ts | 2 +- web/src/routes/blog/$slug.tsx | 2 + web/src/routes/blog/index.tsx | 20 +++ web/src/routes/terms.tsx | 24 +-- web/vite.config.ts | 9 +- 65 files changed, 449 insertions(+), 698 deletions(-) rename {website => docs}/.gitignore (100%) rename {website => docs}/README.md (100%) rename {website => docs}/docs/advanced/_category_.json (100%) rename {website => docs}/docs/advanced/api-reference.mdx (100%) rename {website => docs}/docs/advanced/comparison.mdx (100%) rename {website => docs}/docs/advanced/devtools.mdx (100%) rename {website => docs}/docs/advanced/migration.mdx (100%) rename {website => docs}/docs/advanced/observers.mdx (100%) rename {website => docs}/docs/api/_category_.json (100%) rename {website => docs}/docs/api/infinite-queries.mdx (100%) rename {website => docs}/docs/api/mutations.mdx (100%) rename {website => docs}/docs/api/queries.mdx (100%) rename {website => docs}/docs/api/query-client.mdx (100%) rename {website => docs}/docs/getting-started/_category_.json (100%) rename {website => docs}/docs/getting-started/installation.mdx (100%) rename {website => docs}/docs/getting-started/quick-start.mdx (100%) rename {website => docs}/docs/guides/_category_.json (100%) rename {website => docs}/docs/guides/caching.mdx (100%) rename {website => docs}/docs/guides/error-handling.mdx (100%) rename {website => docs}/docs/guides/persistence.mdx (100%) rename {website => docs}/docs/guides/query-keys.mdx (100%) rename {website => docs}/docs/guides/retry.mdx (100%) rename {website => docs}/docs/guides/select-pattern.mdx (100%) rename {website => docs}/docs/intro.mdx (100%) rename {website => docs}/docusaurus.config.ts (100%) rename {website => docs}/package-lock.json (100%) rename {website => docs}/package.json (100%) rename {website => docs}/sidebars.ts (100%) rename {website => docs}/src/css/custom.css (100%) rename {website => docs}/src/theme/Logo/index.tsx (100%) rename {website => docs}/static/.nojekyll (100%) rename {website => docs}/static/img/favicon.ico (100%) rename {website => docs}/static/img/logo.svg (100%) rename {website => docs}/tsconfig.json (100%) delete mode 100755 web/.vite-hooks/pre-commit delete mode 100644 web/src/components/code-block.tsx create mode 100644 web/src/components/landing/rust-tint.tsx create mode 100644 web/src/content/blog/cache-policies-explained.mdx delete mode 100644 web/src/content/blog/cache-policies-explained.tsx create mode 100644 web/src/content/blog/cooperative-cancellation.mdx delete mode 100644 web/src/content/blog/cooperative-cancellation.tsx create mode 100644 web/src/content/blog/why-gpui-query.mdx delete mode 100644 web/src/content/blog/why-gpui-query.tsx diff --git a/crates/gpui-query-http/src/cache.rs b/crates/gpui-query-http/src/cache.rs index 8c2f01b..82997ba 100644 --- a/crates/gpui-query-http/src/cache.rs +++ b/crates/gpui-query-http/src/cache.rs @@ -120,10 +120,7 @@ impl HttpCache { && meta.stored_at + meta.fresh_for > SystemTime::now() { let body = { - let guard = self - .bodies - .lock() - .map_err(|_| HttpError::Poisoned)?; + let guard = self.bodies.lock().map_err(|_| HttpError::Poisoned)?; guard.get(url).cloned() }; if let Some(body) = body { @@ -148,10 +145,7 @@ impl HttpCache { // (e) 304 — revalidation succeeded: serve the cached body. if resp.status == 304 { let body = { - let guard = self - .bodies - .lock() - .map_err(|_| HttpError::Poisoned)?; + let guard = self.bodies.lock().map_err(|_| HttpError::Poisoned)?; guard.get(url).cloned() }; let Some(body) = body else { @@ -207,17 +201,11 @@ impl HttpCache { // Store under both locks, dropping each guard before yielding. { - let mut bodies = self - .bodies - .lock() - .map_err(|_| HttpError::Poisoned)?; + let mut bodies = self.bodies.lock().map_err(|_| HttpError::Poisoned)?; bodies.insert(url.to_string(), body.clone()); } { - let mut meta_guard = self - .meta - .lock() - .map_err(|_| HttpError::Poisoned)?; + let mut meta_guard = self.meta.lock().map_err(|_| HttpError::Poisoned)?; meta_guard.insert(url.to_string(), meta.clone()); } diff --git a/crates/gpui-query-persist/tests/file_persister.rs b/crates/gpui-query-persist/tests/file_persister.rs index c931c28..c2ec4c2 100644 --- a/crates/gpui-query-persist/tests/file_persister.rs +++ b/crates/gpui-query-persist/tests/file_persister.rs @@ -224,17 +224,20 @@ fn file_persister_large_snapshot_round_trips() { .unwrap_or_else(|| panic!("format {fmt:?}: sampled entry {sample_key} present")); assert_eq!(entry.cached_at, 1_700_000_000_000 + 9000); assert_eq!(entry.cache_policy, CachePolicy::Ttl { ttl_ms: 60_000 }); - assert_eq!(entry.value, serde_json::json!({ - "id": 9000, - "name": "User 9000", - "email": "user9000@example.test", - "active": true, - "tags": ["tag-9000", "member", "persisted"], - "profile": { - "bio": "Auto-generated biography for user 9000.", - "followers": 27000, - }, - })); + assert_eq!( + entry.value, + serde_json::json!({ + "id": 9000, + "name": "User 9000", + "email": "user9000@example.test", + "active": true, + "tags": ["tag-9000", "member", "persisted"], + "profile": { + "bio": "Auto-generated biography for user 9000.", + "followers": 27000, + }, + }) + ); } } @@ -246,10 +249,17 @@ fn file_persister_corrupt_bincode_yields_empty_snapshot() { // an empty snapshot without panicking or erroring. let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("cache.bin"); - std::fs::write(&path, b"\x00\x01\x02 this is definitely not bincode \xff\xfe garbage").expect("write garbage"); + std::fs::write( + &path, + b"\x00\x01\x02 this is definitely not bincode \xff\xfe garbage", + ) + .expect("write garbage"); let p = FilePersister::bincode(&path); let loaded = pollster::block_on(p.load()).expect("tolerant load"); - assert!(loaded.entries.is_empty(), "corrupt bincode → empty snapshot"); + assert!( + loaded.entries.is_empty(), + "corrupt bincode → empty snapshot" + ); assert_eq!(loaded.version, PERSIST_VERSION); } diff --git a/crates/gpui-query/src/client/bucket/ops.rs b/crates/gpui-query/src/client/bucket/ops.rs index 382bd94..ad11e9f 100644 --- a/crates/gpui-query/src/client/bucket/ops.rs +++ b/crates/gpui-query/src/client/bucket/ops.rs @@ -56,9 +56,7 @@ impl QueryBu if entry.loading { return None; } - if entry.entity.upgrade().is_none() { - return None; - } + entry.entity.upgrade()?; Some((key, entry.last_updated_ms.unwrap_or(0))) }) .min_by_key(|&(_, age)| age); diff --git a/crates/gpui-query/src/client/infinite_bucket.rs b/crates/gpui-query/src/client/infinite_bucket.rs index 25b8881..923c178 100644 --- a/crates/gpui-query/src/client/infinite_bucket.rs +++ b/crates/gpui-query/src/client/infinite_bucket.rs @@ -114,9 +114,9 @@ impl Infinit /// Evict the oldest (least-recently-updated) entry to make room (#1 fix). /// /// **M2 (O(n)→O(1) entity reads)**: scans entry MIRRORS (no `entity.read`) - /// + `WeakEntity::upgrade` liveness, then one `entity.read` on the winner - /// to confirm `!is_loading()` (guards #109 against a stale mirror) and - /// read the authoritative timestamp. Mirrors + /// plus `WeakEntity::upgrade` liveness, then one `entity.read` on the + /// winner to confirm `!is_loading()` (guards #109 against a stale mirror) + /// and read the authoritative timestamp. Mirrors /// [`QueryBucket::evict_oldest`](super::QueryBucket::evict_oldest). pub(crate) fn evict_oldest(&mut self, cx: &App) { loop { @@ -127,9 +127,7 @@ impl Infinit if entry.loading { return None; } - if entry.entity.upgrade().is_none() { - return None; - } + entry.entity.upgrade()?; Some((key, entry.last_updated_ms.unwrap_or(0))) }) .min_by_key(|&(_, age)| age); diff --git a/crates/gpui-query/src/client/mod.rs b/crates/gpui-query/src/client/mod.rs index 5355964..1af652a 100644 --- a/crates/gpui-query/src/client/mod.rs +++ b/crates/gpui-query/src/client/mod.rs @@ -216,7 +216,7 @@ impl QueryClient { self.last_gc_ms = now_ms; return; } - if now_ms.saturating_sub(self.last_gc_ms) < MIN_GC_TIME_MS as u64 { + if now_ms.saturating_sub(self.last_gc_ms) < MIN_GC_TIME_MS { return; } self.last_gc_ms = now_ms; @@ -327,11 +327,11 @@ impl QueryClient { /// Downcast an erased query bucket to `&mut QueryBucket`, recreating /// it in place on the (impossible) type mismatch. /// - /// **M4**: this replaces the 5x duplicated `TypeId` pre-check + `eprintln` - /// + fresh-bucket + `downcast_mut` match block. `Any::downcast_mut` checks + /// **M4**: this replaces the 5x duplicated `TypeId` pre-check, `eprintln`, + /// fresh-bucket, and `downcast_mut` match block. `Any::downcast_mut` checks /// `TypeId` internally, so the explicit pre-check was redundant; we now /// downcast once and, only on the (impossible-after-construction) `None`, - /// log + swap in a fresh typed bucket and downcast *that* (which always + /// log, swap in a fresh typed bucket, and downcast *that* (which always /// succeeds). No production panic. fn bucket_or_recreate( bucket: &mut Box, diff --git a/crates/gpui-query/src/client/mutation_bucket.rs b/crates/gpui-query/src/client/mutation_bucket.rs index 310717a..662a27b 100644 --- a/crates/gpui-query/src/client/mutation_bucket.rs +++ b/crates/gpui-query/src/client/mutation_bucket.rs @@ -91,7 +91,6 @@ pub struct MutationBucket { /// (Audit #41) The previous private `now_ms()` duplicate is removed; this /// module now reuses the canonical [`super::erased::current_time_ms`]. - impl< V: Clone + Send + Sync + 'static, T: Clone + Send + Sync + 'static, @@ -109,7 +108,7 @@ impl< /// Evict the oldest (least-recently-updated) entry to make room for a new one. /// /// **M2 (O(n)→O(1) entity reads)**: scans entry MIRRORS (no `entity.read`) - /// + `WeakEntity::upgrade` liveness, picking the entry with the smallest + /// plus `WeakEntity::upgrade` liveness, picking the entry with the smallest /// mirrored recency — preferring the completion-time mirror /// (`last_updated_ms`) and falling back to the insertion time /// (`updated_at`) for never-completed mutations (audit #112). Then **one** @@ -127,9 +126,7 @@ impl< if entry.loading { return None; } - if entry.entity.upgrade().is_none() { - return None; - } + entry.entity.upgrade()?; // Prefer completion-time mirror; fall back to insertion // time for never-completed mutations (audit #112). let age = entry.last_updated_ms.unwrap_or(entry.updated_at); diff --git a/crates/gpui-query/src/client/prepared_fetch.rs b/crates/gpui-query/src/client/prepared_fetch.rs index 3596326..c1a6759 100644 --- a/crates/gpui-query/src/client/prepared_fetch.rs +++ b/crates/gpui-query/src/client/prepared_fetch.rs @@ -61,8 +61,7 @@ impl Prepare /// re-syscalling `current_time_ms()`. pub fn complete_success(self, data: T, cx: &mut App) { self.entity.update(cx, |resource, cx| { - let accepted = - resource.complete_current_success(self.request_id, data, self.now_ms); + let accepted = resource.complete_current_success(self.request_id, data, self.now_ms); // B2: precise dirty signal for the persistence layer. Imperative // completions (`prepare_fetch_query`) mutate the cache just like the // hook-layer completions, so they must wake `persist_with` too — @@ -86,8 +85,7 @@ impl Prepare /// re-syscalling `current_time_ms()`. pub fn complete_failure(self, error: E, cx: &mut App) { self.entity.update(cx, |resource, cx| { - let accepted = - resource.complete_current_failure(self.request_id, error, self.now_ms); + let accepted = resource.complete_current_failure(self.request_id, error, self.now_ms); // B2: see `complete_success` — bump only when the failure was // actually accepted, so an imperative failure is visible to // `persist_with` without spurious saves on stale completions. diff --git a/crates/gpui-query/src/tests/core_cache/mod.rs b/crates/gpui-query/src/tests/core_cache/mod.rs index 1b5e97a..94a9511 100644 --- a/crates/gpui-query/src/tests/core_cache/mod.rs +++ b/crates/gpui-query/src/tests/core_cache/mod.rs @@ -37,9 +37,9 @@ pub(crate) const STALE_MS: u64 = 2_000; pub(crate) const SWR_TOTAL_MS: u64 = TTL_MS + STALE_MS; // 3_000 // Derived boundary offsets from STORED_AT_MS: -pub(crate) const AT_TTL_BOUNDARY: u64 = STORED_AT_MS + TTL_MS as u64; // 2_000 — exactly at TTL edge +pub(crate) const AT_TTL_BOUNDARY: u64 = STORED_AT_MS + TTL_MS; // 2_000 — exactly at TTL edge pub(crate) const ONE_MS_PAST_TTL: u64 = AT_TTL_BOUNDARY + 1; // 2_001 — just past TTL -pub(crate) const AT_SWR_BOUNDARY: u64 = STORED_AT_MS + SWR_TOTAL_MS as u64; // 4_000 — exactly at total edge +pub(crate) const AT_SWR_BOUNDARY: u64 = STORED_AT_MS + SWR_TOTAL_MS; // 4_000 — exactly at total edge pub(crate) const ONE_MS_PAST_SWR: u64 = AT_SWR_BOUNDARY + 1; // 4_001 — fully expired // ── Helpers ────────────────────────────────────────────────────────────── diff --git a/crates/gpui-query/src/tests/core_infinite_query/max_pages.rs b/crates/gpui-query/src/tests/core_infinite_query/max_pages.rs index e2b8ee6..5cbaa7a 100644 --- a/crates/gpui-query/src/tests/core_infinite_query/max_pages.rs +++ b/crates/gpui-query/src/tests/core_infinite_query/max_pages.rs @@ -96,11 +96,11 @@ fn max_pages_50_allows_50_pages_and_evicts_on_51st() { ]; // Load 50 pages — all with has_more=true so has_next_page stays true - for i in 0..50 { + for (i, label) in P_LABELS.iter().enumerate().take(50) { let id = r.begin_fetch_next(&mut seq, (i * 100) as u64).unwrap(); r.complete_page_success( id, - vec![P_LABELS[i]], + vec![*label], true, // always report more pages available true, ((i + 1) * 100) as u64, diff --git a/crates/gpui-query/src/tests/integration_client_coverage/client_operations/persist_with_hydrate.rs b/crates/gpui-query/src/tests/integration_client_coverage/client_operations/persist_with_hydrate.rs index 43e16c6..e686b97 100644 --- a/crates/gpui-query/src/tests/integration_client_coverage/client_operations/persist_with_hydrate.rs +++ b/crates/gpui-query/src/tests/integration_client_coverage/client_operations/persist_with_hydrate.rs @@ -17,7 +17,7 @@ use crate::core::{ InfiniteQueryResource, MutationResource, QueryError, QueryKey, QueryResource, QueryStatus, }; use crate::hook::{ - fetch_query, mutate, use_infinite_query, use_mutation, use_query_manual, InfiniteQueryOptions, + InfiniteQueryOptions, fetch_query, mutate, use_infinite_query, use_mutation, use_query_manual, }; use crate::tests::test_support::*; @@ -168,7 +168,7 @@ fn test_persist_with_saves_on_mutation(cx: &mut TestAppContext) { // the snapshot. The harness holds it for the life of the test, mirroring a // real component holding the `Entity` from `use_query`. struct H { - entity: Entity>, + _entity: Entity>, _handle: PersistHandle, } let harness = cx.new(|cx| { @@ -195,7 +195,10 @@ fn test_persist_with_saves_on_mutation(cx: &mut TestAppContext) { }); (handle, entity) }); - H { entity, _handle } + H { + _entity: entity, + _handle, + } }); // A mutation (on another key) bumps the dirty signal → persist_with collects @@ -245,7 +248,7 @@ fn test_hydrate_primes_via_deserializer_registry(cx: &mut TestAppContext) { // harness; hydrate's `set_query_data` then reuses the retained entity rather // than creating one that is immediately dropped. struct H { - entity: Entity>, + _entity: Entity>, } let harness = cx.new(|cx| { cx.update_global::(|client, _cx| { @@ -255,7 +258,7 @@ fn test_hydrate_primes_via_deserializer_registry(cx: &mut TestAppContext) { let entity = cx.update_global::(|client, cx| { client.resource::(QueryKey::from("hydrate_k"), cx) }); - H { entity } + H { _entity: entity } }); // Run the async hydrate on the background executor, then assert priming. @@ -448,7 +451,7 @@ fn test_persist_with_driven_by_real_mutation_completion(cx: &mut TestAppContext) struct H { // A retained Success query entry — the thing actually persisted. - query: Entity>, + _query: Entity>, // The mutation entity; completing it bumps the dirty signal. mutation: Entity>, _handle: PersistHandle, @@ -479,7 +482,7 @@ fn test_persist_with_driven_by_real_mutation_completion(cx: &mut TestAppContext) // Create the mutation via the REAL hook path. let (mutation, _msub) = use_mutation::((), cx); H { - query, + _query: query, mutation, _handle, } @@ -563,10 +566,7 @@ fn test_persist_with_driven_by_real_infinite_completion(cx: &mut TestAppContext) |_last_page| async move { Ok::<_, QueryError>((vec!["page-0".to_string()], true)) }, cx, ); - H { - infinite, - _handle, - } + H { infinite, _handle } }); // Let the first-page fetch resolve → bumps `CacheMutation` → `persist_with` @@ -706,7 +706,7 @@ fn test_persist_with_debounce_coalesces(cx: &mut TestAppContext) { // Hold a live Success entry on the "coalesced" key so the snapshot actually // contains something to save (the bucket stores only WeakEntity). struct H { - entity: Entity>, + _entity: Entity>, _handle: PersistHandle, } let harness = cx.new(|cx| { @@ -728,7 +728,10 @@ fn test_persist_with_debounce_coalesces(cx: &mut TestAppContext) { }); (handle, entity) }); - H { entity, _handle } + H { + _entity: entity, + _handle, + } }); // Fire N>=3 rapid mutations on the same driver key. Each is issued in its @@ -758,7 +761,8 @@ fn test_persist_with_debounce_coalesces(cx: &mut TestAppContext) { // the pending timer tasks; a subsequent `run_until_parked` lets exactly one // of them drain the shared slot and run `save`. The remaining tasks wake to // find the slot already empty and no-op. - cx.background_executor.advance_clock(debounce + Duration::from_millis(1)); + cx.background_executor + .advance_clock(debounce + Duration::from_millis(1)); cx.run_until_parked(); assert_eq!( @@ -788,15 +792,9 @@ fn test_persist_with_debounce_coalesces(cx: &mut TestAppContext) { fn block_on_ready(fut: impl std::future::Future) -> R { use std::future::Future; use std::pin::Pin; - use std::sync::Arc; - use std::task::{Context, Poll, Wake, Waker}; + use std::task::{Context, Poll, Waker}; - struct NoWake; - impl Wake for NoWake { - fn wake(self: Arc) {} - } - let waker = Waker::from(Arc::new(NoWake)); - let mut cx = Context::from_waker(&waker); + let mut cx = Context::from_waker(Waker::noop()); let mut fut = Box::pin(fut); // SAFETY: pinned on the heap; we hold the only reference. let mut pinned: Pin<&mut dyn Future> = Pin::as_mut(&mut fut); diff --git a/crates/gpui-query/src/tests/property_tests/query_key/proptests.rs b/crates/gpui-query/src/tests/property_tests/query_key/proptests.rs index 7c072cb..044bd8c 100644 --- a/crates/gpui-query/src/tests/property_tests/query_key/proptests.rs +++ b/crates/gpui-query/src/tests/property_tests/query_key/proptests.rs @@ -15,9 +15,10 @@ use super::strategies::*; // default `cargo test` run cheap. The heavyweight long-key / very-long-string // invariants are also covered by the deterministic_tests module. fn test_config() -> ProptestConfig { - let mut config = ProptestConfig::default(); - config.cases = 64; - config + ProptestConfig { + cases: 64, + ..ProptestConfig::default() + } } // ── 1. Equality ───────────────────────────────────────────────────────── diff --git a/crates/gpui-query/src/tests/test_support.rs b/crates/gpui-query/src/tests/test_support.rs index f8c60a3..f4a85e7 100644 --- a/crates/gpui-query/src/tests/test_support.rs +++ b/crates/gpui-query/src/tests/test_support.rs @@ -118,6 +118,7 @@ pub fn test_sequencer() -> RequestSequencer { /// Uses `CachePolicy::NoCache` so every `begin_request` returns `Started` /// (never `CacheHit`), giving deterministic control over each fetch lifecycle /// step without worrying about TTL freshness windows. +#[expect(dead_code, reason = "kept as a shared lifecycle-test helper")] pub fn resource_with_sequencer( key: impl Into, ) -> (QueryResource<&'static str>, RequestSequencer) { diff --git a/website/.gitignore b/docs/.gitignore similarity index 100% rename from website/.gitignore rename to docs/.gitignore diff --git a/website/README.md b/docs/README.md similarity index 100% rename from website/README.md rename to docs/README.md diff --git a/website/docs/advanced/_category_.json b/docs/docs/advanced/_category_.json similarity index 100% rename from website/docs/advanced/_category_.json rename to docs/docs/advanced/_category_.json diff --git a/website/docs/advanced/api-reference.mdx b/docs/docs/advanced/api-reference.mdx similarity index 100% rename from website/docs/advanced/api-reference.mdx rename to docs/docs/advanced/api-reference.mdx diff --git a/website/docs/advanced/comparison.mdx b/docs/docs/advanced/comparison.mdx similarity index 100% rename from website/docs/advanced/comparison.mdx rename to docs/docs/advanced/comparison.mdx diff --git a/website/docs/advanced/devtools.mdx b/docs/docs/advanced/devtools.mdx similarity index 100% rename from website/docs/advanced/devtools.mdx rename to docs/docs/advanced/devtools.mdx diff --git a/website/docs/advanced/migration.mdx b/docs/docs/advanced/migration.mdx similarity index 100% rename from website/docs/advanced/migration.mdx rename to docs/docs/advanced/migration.mdx diff --git a/website/docs/advanced/observers.mdx b/docs/docs/advanced/observers.mdx similarity index 100% rename from website/docs/advanced/observers.mdx rename to docs/docs/advanced/observers.mdx diff --git a/website/docs/api/_category_.json b/docs/docs/api/_category_.json similarity index 100% rename from website/docs/api/_category_.json rename to docs/docs/api/_category_.json diff --git a/website/docs/api/infinite-queries.mdx b/docs/docs/api/infinite-queries.mdx similarity index 100% rename from website/docs/api/infinite-queries.mdx rename to docs/docs/api/infinite-queries.mdx diff --git a/website/docs/api/mutations.mdx b/docs/docs/api/mutations.mdx similarity index 100% rename from website/docs/api/mutations.mdx rename to docs/docs/api/mutations.mdx diff --git a/website/docs/api/queries.mdx b/docs/docs/api/queries.mdx similarity index 100% rename from website/docs/api/queries.mdx rename to docs/docs/api/queries.mdx diff --git a/website/docs/api/query-client.mdx b/docs/docs/api/query-client.mdx similarity index 100% rename from website/docs/api/query-client.mdx rename to docs/docs/api/query-client.mdx diff --git a/website/docs/getting-started/_category_.json b/docs/docs/getting-started/_category_.json similarity index 100% rename from website/docs/getting-started/_category_.json rename to docs/docs/getting-started/_category_.json diff --git a/website/docs/getting-started/installation.mdx b/docs/docs/getting-started/installation.mdx similarity index 100% rename from website/docs/getting-started/installation.mdx rename to docs/docs/getting-started/installation.mdx diff --git a/website/docs/getting-started/quick-start.mdx b/docs/docs/getting-started/quick-start.mdx similarity index 100% rename from website/docs/getting-started/quick-start.mdx rename to docs/docs/getting-started/quick-start.mdx diff --git a/website/docs/guides/_category_.json b/docs/docs/guides/_category_.json similarity index 100% rename from website/docs/guides/_category_.json rename to docs/docs/guides/_category_.json diff --git a/website/docs/guides/caching.mdx b/docs/docs/guides/caching.mdx similarity index 100% rename from website/docs/guides/caching.mdx rename to docs/docs/guides/caching.mdx diff --git a/website/docs/guides/error-handling.mdx b/docs/docs/guides/error-handling.mdx similarity index 100% rename from website/docs/guides/error-handling.mdx rename to docs/docs/guides/error-handling.mdx diff --git a/website/docs/guides/persistence.mdx b/docs/docs/guides/persistence.mdx similarity index 100% rename from website/docs/guides/persistence.mdx rename to docs/docs/guides/persistence.mdx diff --git a/website/docs/guides/query-keys.mdx b/docs/docs/guides/query-keys.mdx similarity index 100% rename from website/docs/guides/query-keys.mdx rename to docs/docs/guides/query-keys.mdx diff --git a/website/docs/guides/retry.mdx b/docs/docs/guides/retry.mdx similarity index 100% rename from website/docs/guides/retry.mdx rename to docs/docs/guides/retry.mdx diff --git a/website/docs/guides/select-pattern.mdx b/docs/docs/guides/select-pattern.mdx similarity index 100% rename from website/docs/guides/select-pattern.mdx rename to docs/docs/guides/select-pattern.mdx diff --git a/website/docs/intro.mdx b/docs/docs/intro.mdx similarity index 100% rename from website/docs/intro.mdx rename to docs/docs/intro.mdx diff --git a/website/docusaurus.config.ts b/docs/docusaurus.config.ts similarity index 100% rename from website/docusaurus.config.ts rename to docs/docusaurus.config.ts diff --git a/website/package-lock.json b/docs/package-lock.json similarity index 100% rename from website/package-lock.json rename to docs/package-lock.json diff --git a/website/package.json b/docs/package.json similarity index 100% rename from website/package.json rename to docs/package.json diff --git a/website/sidebars.ts b/docs/sidebars.ts similarity index 100% rename from website/sidebars.ts rename to docs/sidebars.ts diff --git a/website/src/css/custom.css b/docs/src/css/custom.css similarity index 100% rename from website/src/css/custom.css rename to docs/src/css/custom.css diff --git a/website/src/theme/Logo/index.tsx b/docs/src/theme/Logo/index.tsx similarity index 100% rename from website/src/theme/Logo/index.tsx rename to docs/src/theme/Logo/index.tsx diff --git a/website/static/.nojekyll b/docs/static/.nojekyll similarity index 100% rename from website/static/.nojekyll rename to docs/static/.nojekyll diff --git a/website/static/img/favicon.ico b/docs/static/img/favicon.ico similarity index 100% rename from website/static/img/favicon.ico rename to docs/static/img/favicon.ico diff --git a/website/static/img/logo.svg b/docs/static/img/logo.svg similarity index 100% rename from website/static/img/logo.svg rename to docs/static/img/logo.svg diff --git a/website/tsconfig.json b/docs/tsconfig.json similarity index 100% rename from website/tsconfig.json rename to docs/tsconfig.json diff --git a/web/.vite-hooks/pre-commit b/web/.vite-hooks/pre-commit deleted file mode 100755 index a89414d..0000000 --- a/web/.vite-hooks/pre-commit +++ /dev/null @@ -1 +0,0 @@ -cd web && vp staged diff --git a/web/package.json b/web/package.json index 868fcb0..0271bca 100644 --- a/web/package.json +++ b/web/package.json @@ -11,8 +11,7 @@ "build": "node scripts/build-docs.mjs && vp build && npx pagefind --site dist/client && node scripts/generate-llms-txt.mjs --output dist/client && node scripts/generate-md-alt.mjs --output dist/client", "preview": "vp preview", "test": "vp test run", - "deploy": "bun run build && npx wrangler pages deploy dist/client --project-name=gpui-query", - "prepare": "vp config" + "deploy": "bun run build && npx wrangler pages deploy dist/client --project-name=gpui-query" }, "dependencies": { "@base-ui/react": "^1.5.0", diff --git a/web/src/components/code-block.tsx b/web/src/components/code-block.tsx deleted file mode 100644 index 688cfff..0000000 --- a/web/src/components/code-block.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { CheckIcon, CopyIcon } from "@phosphor-icons/react"; -import { Button } from "#/components/ui/button"; -import { useState, useRef, type ReactNode } from "react"; - -interface CodeBlockProps { - children: ReactNode; - title?: string; -} - -export function CodeBlock({ children, title }: CodeBlockProps) { - const [copied, setCopied] = useState(false); - const codeRef = useRef(null); - - const handleCopy = async () => { - const text = codeRef.current?.textContent ?? ""; - await navigator.clipboard.writeText(text); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - return ( -
- {title && ( -
- {title} -
- )} -
- -
{children}
-
- {copied && ( - - Copied! - - )} -
- ); -} diff --git a/web/src/components/landing/rust-code.tsx b/web/src/components/landing/rust-code.tsx index 37d6402..63d26e1 100644 --- a/web/src/components/landing/rust-code.tsx +++ b/web/src/components/landing/rust-code.tsx @@ -1,67 +1,4 @@ -import type { ReactNode } from "react"; - -/** - * Minimal Rust tinter for landing snippets — two accents only - * (keywords/strings on primary, comments muted), matching the hero style. - */ - -const KEYWORDS = new Set([ - "let", - "use", - "fn", - "impl", - "struct", - "enum", - "match", - "if", - "else", - "return", - "pub", - "mut", - "for", - "in", - "async", - "move", - "await", - "Some", - "None", - "Ok", - "Err", -]); - -const TOKEN = /(\/\/.*$|"[^"]*"|\b[A-Za-z_][A-Za-z0-9_]*\b)/g; - -export function tintRust(line: string, lineKey: string): ReactNode[] { - return tintLine(line, lineKey); -} - -function tintLine(line: string, lineKey: string): ReactNode[] { - return line.split(TOKEN).map((part, i) => { - const key = `${lineKey}-${i}`; - if (part.startsWith("//")) { - return ( - - {part} - - ); - } - if (part.startsWith('"')) { - return ( - - {part} - - ); - } - if (KEYWORDS.has(part)) { - return ( - - {part} - - ); - } - return {part}; - }); -} +import { tintRust } from "./rust-tint"; interface RustCodeProps { code: string; @@ -83,7 +20,7 @@ export function RustCode({ code, lineNumbers = false, className = "" }: RustCode {i + 1} )} - {tintLine(line, `l-${String(i)}`)} + {tintRust(line, `l-${String(i)}`)} ))} diff --git a/web/src/components/landing/rust-tint.tsx b/web/src/components/landing/rust-tint.tsx new file mode 100644 index 0000000..afd40a0 --- /dev/null +++ b/web/src/components/landing/rust-tint.tsx @@ -0,0 +1,79 @@ +import type { ReactNode } from "react"; + +/** + * Minimal Rust tinter for landing snippets - two accents only + * (keywords/strings on primary, comments muted), matching the hero style. + */ +const KEYWORDS = new Set([ + "let", + "use", + "fn", + "impl", + "struct", + "enum", + "match", + "if", + "else", + "return", + "pub", + "mut", + "for", + "in", + "async", + "move", + "await", + "Some", + "None", + "Ok", + "Err", +]); + +const TOKEN = /(\/\/.*$|"[^"]*"|\b[A-Za-z_][A-Za-z0-9_]*\b)/g; + +export function tintRust(line: string, lineKey: string): ReactNode[] { + return tokenizeLine(line).map(({ part, offset }) => { + const key = `${lineKey}-${offset}`; + if (part.startsWith("//")) { + return ( + + {part} + + ); + } + if (part.startsWith('"')) { + return ( + + {part} + + ); + } + if (KEYWORDS.has(part)) { + return ( + + {part} + + ); + } + return {part}; + }); +} + +function tokenizeLine(line: string): Array<{ part: string; offset: number }> { + const parts: Array<{ part: string; offset: number }> = []; + let lastIndex = 0; + + for (const match of line.matchAll(TOKEN)) { + const offset = match.index ?? 0; + if (offset > lastIndex) { + parts.push({ part: line.slice(lastIndex, offset), offset: lastIndex }); + } + parts.push({ part: match[0], offset }); + lastIndex = offset + match[0].length; + } + + if (lastIndex < line.length) { + parts.push({ part: line.slice(lastIndex), offset: lastIndex }); + } + + return parts; +} diff --git a/web/src/components/landing/v3.tsx b/web/src/components/landing/v3.tsx index d92300a..67bfad4 100644 --- a/web/src/components/landing/v3.tsx +++ b/web/src/components/landing/v3.tsx @@ -2,7 +2,7 @@ import { ArrowRightIcon, GithubLogoIcon } from "@phosphor-icons/react"; import { useCallback, useRef, useState } from "react"; import { Button } from "#/components/ui/button"; import { CommandLine } from "./decor"; -import { tintRust } from "./rust-code"; +import { tintRust } from "./rust-tint"; import { VersionSwitcher } from "./version-switcher"; /* ══ V3 · BLUEPRINT ═════════════════════════════════════════════════ diff --git a/web/src/components/landing/version-switcher.tsx b/web/src/components/landing/version-switcher.tsx index e6996c2..79e72f7 100644 --- a/web/src/components/landing/version-switcher.tsx +++ b/web/src/components/landing/version-switcher.tsx @@ -1,12 +1,12 @@ /** Floating switcher for comparing landing page previews. Remove once a winner ships. */ -export function VersionSwitcher({ current }: { current: "v1" | "v2" | "v3" | "v4" }) { - const versions = [ - { id: "v1", label: "V1 signal path", href: "/v1" }, - { id: "v2", label: "V2 control room", href: "/v2" }, - { id: "v3", label: "V3 blueprint", href: "/v3" }, - { id: "v4", label: "V4 combined", href: "/v4" }, - ] as const; +const VERSIONS = [ + { id: "v1", label: "V1 signal path", href: "/v1" }, + { id: "v2", label: "V2 control room", href: "/v2" }, + { id: "v3", label: "V3 blueprint", href: "/v3" }, + { id: "v4", label: "V4 combined", href: "/v4" }, +] as const; +export function VersionSwitcher({ current }: { current: "v1" | "v2" | "v3" | "v4" }) { return (