Saanora for Windows
The full Mark experience — answers, research with cited sources, writing, and code — one Alt+Space away from anything you're doing.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Always. Give me the symptom and the table size and we’ll work backwards from there.
Timeline widgets almost always sort by created_at inside one tenant, which is exactly where this tends to go wrong.
Find the bottleneck
Run EXPLAIN (ANALYZE, BUFFERS)first. It’s almost always one of three shapes:
- Seq Scan on orders — no usable index, so all 40M rows get read.
- Rows Removed by Filter in the thousands — the index is too broad.
- Sort with
diskin the method — you outgrewwork_mem.
The fix that usually holds
A composite index in selectivity order, built without blocking writes:
CREATE INDEX CONCURRENTLY idx_orders_tenant_created ON orders (tenant_id, created_at DESC) INCLUDE (status, total_cents);
That turns the Seq Scan into an Index Only Scan. Run ANALYZE orders after.
Mark can make mistakes. Verify important info.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Five questions, exam style. You get the explanation after each one — and anything you miss becomes a flashcard at the end.
Mark can make mistakes. Verify important info.
Five questions, exam style. You get the explanation after each one — and anything you miss becomes a flashcard at the end.
Mark can make mistakes. Verify important info.
Five questions, exam style. You get the explanation after each one — and anything you miss becomes a flashcard at the end.
Mark can make mistakes. Verify important info.
Five questions, exam style. You get the explanation after each one — and anything you miss becomes a flashcard at the end.
Mark can make mistakes. Verify important info.
Mark, over anything.
No window to find, no tab to hunt for. Press it over your editor, a spreadsheet, a PDF or a game — Mark appears on top, answers, and gets out of the way. Press it again and it's gone.
Not a chatbot in a box. A place to think.
Answers with receipts
Switch on Research and Mark searches the live web before it answers — then shows you every source behind every claim, so you can check the work instead of taking it on faith.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
Then anchor it on generation share rather than installed capacity — capacity flatters solar, and your LPs will know it.
The short answer
Solar supplied about 6.9% of global electricity in 2024 — 2,131 TWh, up from 5.5% — and was the largest source of new generation for the third year running.
How fast it’s moving
- Output grew 29% year on year — about 474 TWh of new generation.
- China installed 277 GW, over half of everything added worldwide.
- Wind and solar together crossed 15% of world electricity.
- Installed PV capacity passed 2 TW — the second terawatt took two years.
The IEA expects solar PV to deliver ~80% of renewable capacity growth to 2030.
Mark can make mistakes. Verify important info.
See it, don't just read it
Molecules, orbits, functions and diagrams render as real objects inside the conversation. Drag to rotate, hover to label, change a parameter and watch it respond.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Kepler’s third law: orbital period grows with the 3/2 power of the radius, so distance costs time faster than it costs space.
Orbital radii are true to scale; the bodies are enlarged ~2,000× or Mercury would be a single pixel.
Mercury sits at 0.39 AU, Earth at 1.0, Mars at 1.52 — the gaps widen fast, which is why a Mars transfer window only opens every 26 months.
Mark can make mistakes. Verify important info.
Built for real work
Typed, syntax-highlighted code in 20+ languages — with the reasoning behind the approach, not just the snippet. Copy it out, or keep asking until it fits your codebase.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Here’s a small, typed useDebouncedValue — it works for any value, not just strings, and cleans up after itself:
export function useDebouncedValue<T>(value: T, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { // Reset the timer on every change — only the last value lands. const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }
Using it
- Search-as-you-type —
useDebouncedValue(query, 300), then fetch on the debounced value, not the raw one. - Generic on purpose — debounce filters, sliders, or a whole form object the same way.
Want the fetch side too? I can add an AbortController so stale requests cancel themselves.
Mark can make mistakes. Verify important info.
Everything Mark does,
closer to your keyboard.
Temporary chats
Flip on incognito and a ghost keeps you company — the conversation is never saved and never synced. Close it and it's gone.
Cross-device sync
Chats and settings stay in step across web, desktop, and mobile. Start a thought at your desk, pick it up anywhere.
Tray + Alt+Space
Saanora lives quietly in your system tray. Press Alt+Space and Mark appears over whatever you're doing — no window hunting.
Reply notifications
Ask something long-running, go do other work. A native notification with a soft chime tells you the moment Mark replies.
Interactive answers
3D scenes, charts, diagrams, quizzes, flashcards, live simulations — plus generated PDF, Word, Excel, and PowerPoint files that save straight to Downloads.
Auto-updates
The app keeps itself current in the background. Install once from the Store and new features arrive on their own.
Set up in 3 steps.
Install from the Store
Open the Microsoft Store listing, hit Install, and Windows takes it from there — signed, verified, and kept up to date automatically.
Sign in
Choose Continue with Google — your browser opens, you approve, and the app signs itself in. Email and password work too.
Ask anything
Press Alt+Space and Mark appears from anywhere — over your editor, your browser, your slides. That's it.
Before you install.
Is it really free?
Yes. The desktop app is free and there is no trial timer. Heavier limits and Research are part of a paid plan, but everything you see on this page works on the free tier.
Will Windows warn me when I install it?
No. It ships through the Microsoft Store, so the build is Microsoft-signed and verified — you won't see a SmartScreen prompt or an unknown-publisher warning.
Does it work offline?
The app opens offline, but answers need a connection — Mark runs in the cloud, not on your machine. Your chat history is cached locally so you can still read past conversations.
Do my chats sync with the web and phone?
Yes, automatically. Sign in with the same account and your chats, settings and thinking mode stay in step. Temporary chats are the deliberate exception — they never leave the device.
Is macOS coming?
It's in progress. The app is built on the same codebase as the Windows version, so the wait is about packaging and notarisation rather than rewriting the product.
One Alt+Space away.
Free to install, signed by Microsoft, and it keeps itself up to date.