Skip to content

Commit 7a0ae7c

Browse files
authored
Merge pull request #102 from vectorlessflow/dev
Dev
2 parents c859c62 + 00bc1e9 commit 7a0ae7c

21 files changed

Lines changed: 728 additions & 105 deletions

File tree

docs/docusaurus.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const config: Config = {
7676
{to: '/docs/sdk/python', label: 'Python', position: 'left'},
7777
{to: '/docs/sdk/rust', label: 'Rust', position: 'left'},
7878
{to: '/docs/intro', label: 'Documentation', position: 'left'},
79-
{to: '/blog', label: 'Blog', position: 'left'},
79+
// {to: '/blog', label: 'Blog', position: 'left'},
8080
],
8181
},
8282
prism: {
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import React, { useState, useEffect, useCallback } from 'react';
2+
import styles from './styles.module.css';
3+
4+
const OWNER = 'vectorlessflow';
5+
const REPO = 'vectorless';
6+
7+
function formatNumber(num: number | null): string {
8+
if (num === null) return '—';
9+
return num.toLocaleString();
10+
}
11+
12+
async function fetchRepoBasics(): Promise<number> {
13+
const resp = await fetch(`https://api.github.com/repos/${OWNER}/${REPO}`, {
14+
headers: { Accept: 'application/vnd.github.v3+json' },
15+
});
16+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
17+
const data = await resp.json();
18+
return data.stargazers_count ?? 0;
19+
}
20+
21+
async function fetchOpenIssues(): Promise<number> {
22+
const resp = await fetch(
23+
`https://api.github.com/search/issues?q=repo:${OWNER}/${REPO}+type:issue+state:open&per_page=1`,
24+
{ headers: { Accept: 'application/vnd.github.v3+json' } },
25+
);
26+
if (!resp.ok) throw new Error(`Issues: ${resp.status}`);
27+
const data = await resp.json();
28+
return data.total_count ?? 0;
29+
}
30+
31+
async function fetchOpenPRs(): Promise<number> {
32+
const resp = await fetch(
33+
`https://api.github.com/search/issues?q=repo:${OWNER}/${REPO}+type:pr+state:open&per_page=1`,
34+
{ headers: { Accept: 'application/vnd.github.v3+json' } },
35+
);
36+
if (!resp.ok) throw new Error(`PR: ${resp.status}`);
37+
const data = await resp.json();
38+
return data.total_count ?? 0;
39+
}
40+
41+
export default function GitHubStats(): React.ReactElement {
42+
const [stars, setStars] = useState<number | null>(null);
43+
const [issues, setIssues] = useState<number | null>(null);
44+
const [prs, setPrs] = useState<number | null>(null);
45+
const [error, setError] = useState(false);
46+
47+
const load = useCallback(async () => {
48+
setStars(null);
49+
setIssues(null);
50+
setPrs(null);
51+
setError(false);
52+
try {
53+
const [s, i, p] = await Promise.all([fetchRepoBasics(), fetchOpenIssues(), fetchOpenPRs()]);
54+
setStars(s);
55+
setIssues(i);
56+
setPrs(p);
57+
} catch {
58+
setError(true);
59+
}
60+
}, []);
61+
62+
useEffect(() => {
63+
load();
64+
const t = setInterval(load, 300000);
65+
return () => clearInterval(t);
66+
}, [load]);
67+
68+
return (
69+
<div
70+
className={styles.widget}
71+
onClick={() => window.open('https://github.com/vectorlessflow/vectorless', '_blank', 'noopener,noreferrer')}
72+
role="link"
73+
tabIndex={0}
74+
onKeyDown={(e) => { if (e.key === 'Enter') window.open('https://github.com/vectorlessflow/vectorless', '_blank', 'noopener,noreferrer'); }}>
75+
<div className={styles.statList}>
76+
<div className={styles.statRow}>
77+
<div className={styles.statLabel}>
78+
<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>
79+
Stars
80+
</div>
81+
<div className={styles.statNumber}>
82+
{error ? '—' : formatNumber(stars)}
83+
</div>
84+
</div>
85+
<div className={styles.statRow}>
86+
<div className={styles.statLabel}>
87+
<svg width="12" height="12" viewBox="0 0 24 24" fill="var(--primary-dark)"><circle cx="12" cy="12" r="10"/></svg>
88+
Issues
89+
</div>
90+
<div className={styles.statNumber}>
91+
{error ? '—' : formatNumber(issues)}
92+
</div>
93+
</div>
94+
<div className={styles.statRow}>
95+
<div className={styles.statLabel}>
96+
<svg width="12" height="12" viewBox="0 0 24 24" fill="var(--primary-light)"><path d="M6 3v18h12V3H6zm10 16H8V5h8v14z"/></svg>
97+
PRs
98+
</div>
99+
<div className={styles.statNumber}>
100+
{error ? '—' : formatNumber(prs)}
101+
</div>
102+
</div>
103+
</div>
104+
</div>
105+
);
106+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
.widget {
2+
background: var(--bg-offset);
3+
backdrop-filter: blur(12px);
4+
border-radius: 1.2rem;
5+
border: 1px solid var(--border);
6+
padding: 0.9rem 1.2rem;
7+
width: 130px;
8+
min-width: 130px;
9+
box-sizing: border-box;
10+
transition: all 0.2s ease;
11+
box-shadow: 0 8px 20px -6px rgba(0, 0, 0, 0.1);
12+
cursor: pointer;
13+
}
14+
15+
.widget:hover {
16+
border-color: var(--primary);
17+
}
18+
19+
.statList {
20+
display: flex;
21+
flex-direction: column;
22+
gap: 0.5rem;
23+
}
24+
25+
.statRow {
26+
display: flex;
27+
align-items: center;
28+
justify-content: space-between;
29+
gap: 1rem;
30+
padding: 0.4rem 0;
31+
}
32+
33+
.statLabel {
34+
display: flex;
35+
align-items: center;
36+
gap: 0.5rem;
37+
font-weight: 500;
38+
font-size: 0.8rem;
39+
color: var(--text-light);
40+
white-space: nowrap;
41+
flex-shrink: 0;
42+
}
43+
44+
.statNumber {
45+
font-family: 'JetBrains Mono', monospace;
46+
font-size: 0.9rem;
47+
font-weight: 700;
48+
letter-spacing: -0.3px;
49+
color: var(--text);
50+
white-space: nowrap;
51+
margin-left: auto;
52+
}
53+
54+
.errorText { color: #F87171; }
55+
56+
@media (max-width: 880px) {
57+
.widget {
58+
width: 100%;
59+
min-width: unset;
60+
}
61+
}

docs/src/css/custom.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@
7272
padding: 0 !important;
7373
}
7474

75+
nav.navbar,
76+
.navbar--fixed-top {
77+
border-bottom: none !important;
78+
box-shadow: none !important;
79+
}
80+
81+
.navbar::after,
82+
.navbar::before {
83+
display: none !important;
84+
}
85+
86+
.navbar-sidebar {
87+
background-color: var(--bg) !important;
88+
}
89+
7590
.navbar__inner {
7691
height: 68px !important;
7792
max-width: 1280px;

docs/src/pages/index.module.css

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,24 @@
99
padding: 0;
1010
height: calc(100vh - 68px);
1111
overflow: hidden;
12+
position: relative;
1213
display: flex;
1314
align-items: center;
1415
justify-content: center;
1516
background-color: var(--bg);
16-
background-image:
17-
linear-gradient(rgba(175, 120, 139, 0.06) 1px, transparent 1px),
18-
linear-gradient(90deg, rgba(175, 120, 139, 0.06) 1px, transparent 1px);
19-
background-size: 48px 48px;
2017
font-family: 'Space Grotesk', sans-serif;
2118
color: var(--text);
2219
line-height: 1.5;
2320
}
2421

22+
/* ===== Stats widget (top-right corner) ===== */
23+
.statsCorner {
24+
position: absolute;
25+
top: 20px;
26+
right: 32px;
27+
z-index: 2;
28+
}
29+
2530
/* ===== Hero Container ===== */
2631
.hero {
2732
max-width: 1280px;

docs/src/pages/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {ReactNode} from 'react';
22
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
33
import Layout from '@theme/Layout';
44
import Link from '@docusaurus/Link';
5+
import GitHubStats from '@site/src/components/GitHubStats';
56

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

@@ -34,6 +35,9 @@ function HamsterIcon({size = 14}: {size?: number}) {
3435
function HomepageHeader() {
3536
return (
3637
<header className={styles.heroBanner}>
38+
<div className={styles.statsCorner}>
39+
<GitHubStats />
40+
</div>
3741
<div className={styles.hero}>
3842
{/* Left: Brand + Features */}
3943
<div className={styles.heroContent}>

docs/src/theme/Navbar/index.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import NavbarItem from '@theme/NavbarItem';
55
import NavbarMobileSidebarToggle from '@theme/Navbar/MobileSidebar/Toggle';
66
import useBaseUrl from '@docusaurus/useBaseUrl';
77
import Link from '@docusaurus/Link';
8-
import GitHubStar from '../../components/GitHubStar';
98
import type {Props as NavbarItemConfig} from '@theme/NavbarItem';
109
import styles from './styles.module.css';
1110

@@ -61,9 +60,6 @@ export default function Navbar(): React.ReactElement {
6160
{centerItems.map((item, i) => <NavbarItem {...(item as NavbarItemConfig)} key={i} />)}
6261
</div>
6362
<div className={styles.navbarRight}>
64-
<div className={styles.githubStarWrapper}>
65-
<GitHubStar />
66-
</div>
6763
<ColorModeToggle />
6864
</div>
6965
</div>

docs/src/theme/Navbar/styles.module.css

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,10 @@
3636
font-weight: 600;
3737
font-family: 'Inter', 'Libre Franklin', -apple-system, BlinkMacSystemFont, sans-serif;
3838
letter-spacing: -0.02em;
39-
color: #111827;
39+
color: var(--text);
4040
line-height: 1;
4141
}
4242

43-
[data-theme='dark'] .logo {
44-
color: #D0D6E4;
45-
}
46-
4743
/* Center: navigation links */
4844
.navbarCenter {
4945
position: absolute;
@@ -57,19 +53,16 @@
5753
.navbarCenter :global(.navbar__link) {
5854
font-size: 0.875rem;
5955
font-weight: 400;
60-
color: #374151;
56+
color: var(--text-light);
6157
padding: 0;
6258
text-decoration: none;
6359
transition: opacity 0.15s ease;
6460
}
6561

66-
[data-theme='dark'] .navbarCenter :global(.navbar__link) {
67-
color: #C8D0E0;
68-
}
69-
7062
.navbarCenter :global(.navbar__link:hover) {
7163
opacity: 0.7;
7264
text-decoration: none;
65+
color: var(--primary);
7366
}
7467

7568
.navbarCenter :global(.navbar__link--active) {
@@ -80,7 +73,7 @@
8073
opacity: 1;
8174
}
8275

83-
/* Right: github star */
76+
/* Right: theme toggle */
8477
.navbarRight {
8578
display: flex;
8679
align-items: center;
@@ -90,11 +83,6 @@
9083
padding-right: 24px;
9184
}
9285

93-
.githubStarWrapper {
94-
display: flex;
95-
align-items: center;
96-
}
97-
9886
/* Theme toggle */
9987
.themeToggle {
10088
display: inline-flex;
@@ -105,7 +93,7 @@
10593
border-radius: 8px;
10694
border: 1px solid var(--border);
10795
background: transparent;
108-
color: #374151;
96+
color: var(--text-light);
10997
cursor: pointer;
11098
transition: all 0.15s;
11199
}
@@ -115,15 +103,6 @@
115103
color: var(--primary);
116104
}
117105

118-
[data-theme='dark'] .themeToggle {
119-
color: #C8D0E0;
120-
}
121-
122-
[data-theme='dark'] .themeToggle:hover {
123-
border-color: var(--primary);
124-
color: var(--primary);
125-
}
126-
127106
@media (max-width: 996px) {
128107
.navbarBrand {
129108
padding-left: 16px;

python/src/engine.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use super::error::to_py_err;
1818
use super::graph::PyDocumentGraph;
1919
use super::metrics::PyMetricsReport;
2020
use super::results::{PyIndexResult, PyQueryResult};
21+
use super::streaming::PyStreamingQuery;
2122

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

62+
async fn run_query_stream(engine: Arc<Engine>, ctx: QueryContext) -> PyResult<PyStreamingQuery> {
63+
let rx = engine.query_stream(ctx).await.map_err(to_py_err)?;
64+
Ok(PyStreamingQuery::new(rx))
65+
}
66+
6167
fn run_metrics_report(engine: Arc<Engine>) -> PyMetricsReport {
6268
PyMetricsReport {
6369
inner: engine.metrics_report(),
@@ -186,6 +192,29 @@ impl PyEngine {
186192
future_into_py(py, run_query(engine, query_ctx))
187193
}
188194

195+
/// Query documents with streaming progress events.
196+
///
197+
/// Returns a StreamingQuery async iterator that yields real-time
198+
/// retrieval events as dicts with a ``"type"`` key.
199+
///
200+
/// Args:
201+
/// ctx: QueryContext with query text and scope.
202+
///
203+
/// Returns:
204+
/// StreamingQuery async iterator.
205+
///
206+
/// Raises:
207+
/// VectorlessError: If query setup fails.
208+
fn query_stream<'py>(
209+
&self,
210+
py: Python<'py>,
211+
ctx: &PyQueryContext,
212+
) -> PyResult<Bound<'py, PyAny>> {
213+
let engine = Arc::clone(&self.inner);
214+
let query_ctx = ctx.inner.clone();
215+
future_into_py(py, run_query_stream(engine, query_ctx))
216+
}
217+
189218
/// List all indexed documents.
190219
///
191220
/// Returns:

0 commit comments

Comments
 (0)