Skip to content
Merged

Dev #102

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const config: Config = {
{to: '/docs/sdk/python', label: 'Python', position: 'left'},
{to: '/docs/sdk/rust', label: 'Rust', position: 'left'},
{to: '/docs/intro', label: 'Documentation', position: 'left'},
{to: '/blog', label: 'Blog', position: 'left'},
// {to: '/blog', label: 'Blog', position: 'left'},
],
},
prism: {
Expand Down
106 changes: 106 additions & 0 deletions docs/src/components/GitHubStats/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useState, useEffect, useCallback } from 'react';
import styles from './styles.module.css';

const OWNER = 'vectorlessflow';
const REPO = 'vectorless';

function formatNumber(num: number | null): string {
if (num === null) return '—';
return num.toLocaleString();
}

async function fetchRepoBasics(): Promise<number> {
const resp = await fetch(`https://api.github.com/repos/${OWNER}/${REPO}`, {
headers: { Accept: 'application/vnd.github.v3+json' },
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
return data.stargazers_count ?? 0;
}

async function fetchOpenIssues(): Promise<number> {
const resp = await fetch(
`https://api.github.com/search/issues?q=repo:${OWNER}/${REPO}+type:issue+state:open&per_page=1`,
{ headers: { Accept: 'application/vnd.github.v3+json' } },
);
if (!resp.ok) throw new Error(`Issues: ${resp.status}`);
const data = await resp.json();
return data.total_count ?? 0;
}

async function fetchOpenPRs(): Promise<number> {
const resp = await fetch(
`https://api.github.com/search/issues?q=repo:${OWNER}/${REPO}+type:pr+state:open&per_page=1`,
{ headers: { Accept: 'application/vnd.github.v3+json' } },
);
if (!resp.ok) throw new Error(`PR: ${resp.status}`);
const data = await resp.json();
return data.total_count ?? 0;
}

export default function GitHubStats(): React.ReactElement {
const [stars, setStars] = useState<number | null>(null);
const [issues, setIssues] = useState<number | null>(null);
const [prs, setPrs] = useState<number | null>(null);
const [error, setError] = useState(false);

const load = useCallback(async () => {
setStars(null);
setIssues(null);
setPrs(null);
setError(false);
try {
const [s, i, p] = await Promise.all([fetchRepoBasics(), fetchOpenIssues(), fetchOpenPRs()]);
setStars(s);
setIssues(i);
setPrs(p);
} catch {
setError(true);
}
}, []);

useEffect(() => {
load();
const t = setInterval(load, 300000);
return () => clearInterval(t);
}, [load]);

return (
<div
className={styles.widget}
onClick={() => window.open('https://github.com/vectorlessflow/vectorless', '_blank', 'noopener,noreferrer')}
role="link"
tabIndex={0}
onKeyDown={(e) => { if (e.key === 'Enter') window.open('https://github.com/vectorlessflow/vectorless', '_blank', 'noopener,noreferrer'); }}>
<div className={styles.statList}>
<div className={styles.statRow}>
<div className={styles.statLabel}>
<svg width="12" height="12" viewBox="0 0 24 24" fill="var(--primary)"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
Stars
</div>
<div className={styles.statNumber}>
{error ? '—' : formatNumber(stars)}
</div>
</div>
<div className={styles.statRow}>
<div className={styles.statLabel}>
<svg width="12" height="12" viewBox="0 0 24 24" fill="var(--primary-dark)"><circle cx="12" cy="12" r="10"/></svg>
Issues
</div>
<div className={styles.statNumber}>
{error ? '—' : formatNumber(issues)}
</div>
</div>
<div className={styles.statRow}>
<div className={styles.statLabel}>
<svg width="12" height="12" viewBox="0 0 24 24" fill="var(--primary-light)"><path d="M6 3v18h12V3H6zm10 16H8V5h8v14z"/></svg>
PRs
</div>
<div className={styles.statNumber}>
{error ? '—' : formatNumber(prs)}
</div>
</div>
</div>
</div>
);
}
61 changes: 61 additions & 0 deletions docs/src/components/GitHubStats/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.widget {
background: var(--bg-offset);
backdrop-filter: blur(12px);
border-radius: 1.2rem;
border: 1px solid var(--border);
padding: 0.9rem 1.2rem;
width: 130px;
min-width: 130px;
box-sizing: border-box;
transition: all 0.2s ease;
box-shadow: 0 8px 20px -6px rgba(0, 0, 0, 0.1);
cursor: pointer;
}

.widget:hover {
border-color: var(--primary);
}

.statList {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.statRow {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.4rem 0;
}

.statLabel {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
font-size: 0.8rem;
color: var(--text-light);
white-space: nowrap;
flex-shrink: 0;
}

.statNumber {
font-family: 'JetBrains Mono', monospace;
font-size: 0.9rem;
font-weight: 700;
letter-spacing: -0.3px;
color: var(--text);
white-space: nowrap;
margin-left: auto;
}

.errorText { color: #F87171; }

@media (max-width: 880px) {
.widget {
width: 100%;
min-width: unset;
}
}
15 changes: 15 additions & 0 deletions docs/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@
padding: 0 !important;
}

nav.navbar,
.navbar--fixed-top {
border-bottom: none !important;
box-shadow: none !important;
}

.navbar::after,
.navbar::before {
display: none !important;
}

.navbar-sidebar {
background-color: var(--bg) !important;
}

.navbar__inner {
height: 68px !important;
max-width: 1280px;
Expand Down
13 changes: 9 additions & 4 deletions docs/src/pages/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@
padding: 0;
height: calc(100vh - 68px);
overflow: hidden;
position: relative;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--bg);
background-image:
linear-gradient(rgba(175, 120, 139, 0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(175, 120, 139, 0.06) 1px, transparent 1px);
background-size: 48px 48px;
font-family: 'Space Grotesk', sans-serif;
color: var(--text);
line-height: 1.5;
}

/* ===== Stats widget (top-right corner) ===== */
.statsCorner {
position: absolute;
top: 20px;
right: 32px;
z-index: 2;
}

/* ===== Hero Container ===== */
.hero {
max-width: 1280px;
Expand Down
4 changes: 4 additions & 0 deletions docs/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {ReactNode} from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import GitHubStats from '@site/src/components/GitHubStats';

import styles from './index.module.css';

Expand Down Expand Up @@ -34,6 +35,9 @@ function HamsterIcon({size = 14}: {size?: number}) {
function HomepageHeader() {
return (
<header className={styles.heroBanner}>
<div className={styles.statsCorner}>
<GitHubStats />
</div>
<div className={styles.hero}>
{/* Left: Brand + Features */}
<div className={styles.heroContent}>
Expand Down
4 changes: 0 additions & 4 deletions docs/src/theme/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import NavbarItem from '@theme/NavbarItem';
import NavbarMobileSidebarToggle from '@theme/Navbar/MobileSidebar/Toggle';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Link from '@docusaurus/Link';
import GitHubStar from '../../components/GitHubStar';
import type {Props as NavbarItemConfig} from '@theme/NavbarItem';
import styles from './styles.module.css';

Expand Down Expand Up @@ -61,9 +60,6 @@ export default function Navbar(): React.ReactElement {
{centerItems.map((item, i) => <NavbarItem {...(item as NavbarItemConfig)} key={i} />)}
</div>
<div className={styles.navbarRight}>
<div className={styles.githubStarWrapper}>
<GitHubStar />
</div>
<ColorModeToggle />
</div>
</div>
Expand Down
31 changes: 5 additions & 26 deletions docs/src/theme/Navbar/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,10 @@
font-weight: 600;
font-family: 'Inter', 'Libre Franklin', -apple-system, BlinkMacSystemFont, sans-serif;
letter-spacing: -0.02em;
color: #111827;
color: var(--text);
line-height: 1;
}

[data-theme='dark'] .logo {
color: #D0D6E4;
}

/* Center: navigation links */
.navbarCenter {
position: absolute;
Expand All @@ -57,19 +53,16 @@
.navbarCenter :global(.navbar__link) {
font-size: 0.875rem;
font-weight: 400;
color: #374151;
color: var(--text-light);
padding: 0;
text-decoration: none;
transition: opacity 0.15s ease;
}

[data-theme='dark'] .navbarCenter :global(.navbar__link) {
color: #C8D0E0;
}

.navbarCenter :global(.navbar__link:hover) {
opacity: 0.7;
text-decoration: none;
color: var(--primary);
}

.navbarCenter :global(.navbar__link--active) {
Expand All @@ -80,7 +73,7 @@
opacity: 1;
}

/* Right: github star */
/* Right: theme toggle */
.navbarRight {
display: flex;
align-items: center;
Expand All @@ -90,11 +83,6 @@
padding-right: 24px;
}

.githubStarWrapper {
display: flex;
align-items: center;
}

/* Theme toggle */
.themeToggle {
display: inline-flex;
Expand All @@ -105,7 +93,7 @@
border-radius: 8px;
border: 1px solid var(--border);
background: transparent;
color: #374151;
color: var(--text-light);
cursor: pointer;
transition: all 0.15s;
}
Expand All @@ -115,15 +103,6 @@
color: var(--primary);
}

[data-theme='dark'] .themeToggle {
color: #C8D0E0;
}

[data-theme='dark'] .themeToggle:hover {
border-color: var(--primary);
color: var(--primary);
}

@media (max-width: 996px) {
.navbarBrand {
padding-left: 16px;
Expand Down
29 changes: 29 additions & 0 deletions python/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use super::error::to_py_err;
use super::graph::PyDocumentGraph;
use super::metrics::PyMetricsReport;
use super::results::{PyIndexResult, PyQueryResult};
use super::streaming::PyStreamingQuery;

// ============================================================
// Engine async helpers (named functions to avoid FnOnce HRTB issue)
Expand Down Expand Up @@ -58,6 +59,11 @@ async fn run_get_graph(engine: Arc<Engine>) -> PyResult<Option<PyDocumentGraph>>
Ok(graph.map(|g| PyDocumentGraph { inner: g }))
}

async fn run_query_stream(engine: Arc<Engine>, ctx: QueryContext) -> PyResult<PyStreamingQuery> {
let rx = engine.query_stream(ctx).await.map_err(to_py_err)?;
Ok(PyStreamingQuery::new(rx))
}

fn run_metrics_report(engine: Arc<Engine>) -> PyMetricsReport {
PyMetricsReport {
inner: engine.metrics_report(),
Expand Down Expand Up @@ -186,6 +192,29 @@ impl PyEngine {
future_into_py(py, run_query(engine, query_ctx))
}

/// Query documents with streaming progress events.
///
/// Returns a StreamingQuery async iterator that yields real-time
/// retrieval events as dicts with a ``"type"`` key.
///
/// Args:
/// ctx: QueryContext with query text and scope.
///
/// Returns:
/// StreamingQuery async iterator.
///
/// Raises:
/// VectorlessError: If query setup fails.
fn query_stream<'py>(
&self,
py: Python<'py>,
ctx: &PyQueryContext,
) -> PyResult<Bound<'py, PyAny>> {
let engine = Arc::clone(&self.inner);
let query_ctx = ctx.inner.clone();
future_into_py(py, run_query_stream(engine, query_ctx))
}

/// List all indexed documents.
///
/// Returns:
Expand Down
Loading
Loading